ua_server_discovery_mdns.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  6. * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  7. */
  8. #include "ua_server_internal.h"
  9. #ifdef UA_ENABLE_DISCOVERY_MULTICAST
  10. #ifndef UA_ENABLE_AMALGAMATION
  11. #include "mdnsd/libmdnsd/xht.h"
  12. #include "mdnsd/libmdnsd/sdtxt.h"
  13. #endif
  14. #ifdef _WIN32
  15. /* inet_ntoa is deprecated on MSVC but used for compatibility */
  16. # define _WINSOCK_DEPRECATED_NO_WARNINGS
  17. # include <winsock2.h>
  18. # include <iphlpapi.h>
  19. # include <ws2tcpip.h>
  20. #else
  21. # include <sys/time.h> // for struct timeval
  22. # include <netinet/in.h> // for struct ip_mreq
  23. # if defined(UA_HAS_GETIFADDR)
  24. # include <ifaddrs.h>
  25. # endif /* UA_HAS_GETIFADDR */
  26. # include <net/if.h> /* for IFF_RUNNING */
  27. # include <netdb.h> // for recvfrom in cygwin
  28. #endif
  29. /* FIXME: Is this a required algorithm? Otherwise, reuse hashing for nodeids */
  30. /* Generates a hash code for a string.
  31. * This function uses the ELF hashing algorithm as reprinted in
  32. * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
  33. */
  34. static int
  35. mdns_hash_record(const char *s) {
  36. /* ELF hash uses unsigned chars and unsigned arithmetic for portability */
  37. const unsigned char *name = (const unsigned char *) s;
  38. unsigned long h = 0;
  39. while(*name) {
  40. h = (h << 4) + (unsigned long) (*name++);
  41. unsigned long g;
  42. if((g = (h & 0xF0000000UL)) != 0)
  43. h ^= (g >> 24);
  44. h &= ~g;
  45. }
  46. return (int) h;
  47. }
  48. static struct serverOnNetwork_list_entry *
  49. mdns_record_add_or_get(UA_DiscoveryManager *dm, const char *record, const char *serverName,
  50. size_t serverNameLen, UA_Boolean createNew) {
  51. int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
  52. struct serverOnNetwork_hash_entry *hash_entry = dm->serverOnNetworkHash[hashIdx];
  53. while(hash_entry) {
  54. size_t maxLen;
  55. if(serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
  56. maxLen = hash_entry->entry->serverOnNetwork.serverName.length;
  57. else
  58. maxLen = serverNameLen;
  59. if(strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data,
  60. serverName, maxLen) == 0)
  61. return hash_entry->entry;
  62. hash_entry = hash_entry->next;
  63. }
  64. if(!createNew)
  65. return NULL;
  66. /* not yet in list, create new one */
  67. /* todo: malloc may fail: return a statuscode */
  68. struct serverOnNetwork_list_entry *listEntry = (serverOnNetwork_list_entry*)
  69. UA_malloc(sizeof(struct serverOnNetwork_list_entry));
  70. listEntry->created = UA_DateTime_now();
  71. listEntry->pathTmp = NULL;
  72. listEntry->txtSet = false;
  73. listEntry->srvSet = false;
  74. UA_ServerOnNetwork_init(&listEntry->serverOnNetwork);
  75. listEntry->serverOnNetwork.recordId = dm->serverOnNetworkRecordIdCounter;
  76. listEntry->serverOnNetwork.serverName.length = serverNameLen;
  77. /* todo: malloc may fail: return a statuscode */
  78. listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
  79. memcpy(listEntry->serverOnNetwork.serverName.data, serverName, serverNameLen);
  80. UA_atomic_addUInt32(&dm->serverOnNetworkRecordIdCounter, 1);
  81. if(dm->serverOnNetworkRecordIdCounter == 0)
  82. dm->serverOnNetworkRecordIdLastReset = UA_DateTime_now();
  83. /* add to hash */
  84. /* todo: malloc may fail: return a statuscode */
  85. struct serverOnNetwork_hash_entry *newHashEntry = (struct serverOnNetwork_hash_entry*)
  86. UA_malloc(sizeof(struct serverOnNetwork_hash_entry));
  87. newHashEntry->next = dm->serverOnNetworkHash[hashIdx];
  88. dm->serverOnNetworkHash[hashIdx] = newHashEntry;
  89. newHashEntry->entry = listEntry;
  90. LIST_INSERT_HEAD(&dm->serverOnNetwork, listEntry, pointers);
  91. return listEntry;
  92. }
  93. #ifdef _WIN32
  94. /* see http://stackoverflow.com/a/10838854/869402 */
  95. static IP_ADAPTER_ADDRESSES *
  96. getInterfaces(const UA_Server *server) {
  97. IP_ADAPTER_ADDRESSES* adapter_addresses = NULL;
  98. /* Start with a 16 KB buffer and resize if needed - multiple attempts in
  99. * case interfaces change while we are in the middle of querying them. */
  100. DWORD adapter_addresses_buffer_size = 16 * 1024;
  101. for(size_t attempts = 0; attempts != 3; ++attempts) {
  102. /* todo: malloc may fail: return a statuscode */
  103. adapter_addresses = (IP_ADAPTER_ADDRESSES*)UA_malloc(adapter_addresses_buffer_size);
  104. if(!adapter_addresses) {
  105. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  106. "GetAdaptersAddresses out of memory");
  107. adapter_addresses = NULL;
  108. break;
  109. }
  110. DWORD error = GetAdaptersAddresses(AF_UNSPEC,
  111. GAA_FLAG_SKIP_ANYCAST |
  112. GAA_FLAG_SKIP_DNS_SERVER |
  113. GAA_FLAG_SKIP_FRIENDLY_NAME,
  114. NULL, adapter_addresses,
  115. &adapter_addresses_buffer_size);
  116. if(ERROR_SUCCESS == error) {
  117. break;
  118. } else if (ERROR_BUFFER_OVERFLOW == error) {
  119. /* Try again with the new size */
  120. UA_free(adapter_addresses);
  121. adapter_addresses = NULL;
  122. continue;
  123. }
  124. /* Unexpected error */
  125. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  126. "GetAdaptersAddresses returned an unexpected error. "
  127. "Not setting mDNS A records.");
  128. UA_free(adapter_addresses);
  129. adapter_addresses = NULL;
  130. break;
  131. }
  132. return adapter_addresses;
  133. }
  134. #endif /* _WIN32 */
  135. static UA_Boolean
  136. mdns_is_self_announce(const UA_Server *server, const struct serverOnNetwork_list_entry *entry) {
  137. for (size_t i=0; i<server->config.networkLayersSize; i++) {
  138. UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
  139. if(UA_String_equal(&entry->serverOnNetwork.discoveryUrl,
  140. &nl->discoveryUrl))
  141. return true;
  142. // check discoveryUrl ignoring tailing slash
  143. if (((
  144. nl->discoveryUrl.length == entry->serverOnNetwork.discoveryUrl.length +1 &&
  145. nl->discoveryUrl.data[nl->discoveryUrl.length-1] == '/'
  146. ) || (
  147. entry->serverOnNetwork.discoveryUrl.length == nl->discoveryUrl.length +1 &&
  148. entry->serverOnNetwork.discoveryUrl.data[entry->serverOnNetwork.discoveryUrl.length-1] == '/'
  149. )
  150. ) &&
  151. memcmp(nl->discoveryUrl.data, entry->serverOnNetwork.discoveryUrl.data,
  152. UA_MIN(nl->discoveryUrl.length, entry->serverOnNetwork.discoveryUrl.length)) == 0
  153. ) {
  154. return true;
  155. }
  156. if (nl->discoveryUrl.length == entry->serverOnNetwork.discoveryUrl.length +1 &&
  157. nl->discoveryUrl.data[nl->discoveryUrl.length-1] == '/' &&
  158. memcmp(nl->discoveryUrl.data, entry->serverOnNetwork.discoveryUrl.data, nl->discoveryUrl.length-1) == 0
  159. ) {
  160. return true;
  161. }
  162. }
  163. /* The discovery URL may also just contain the IP address, but in our
  164. * discovery URL we are using hostname thus previous check may not detect
  165. * the same URL. Therefore we also check if the name matches: */
  166. UA_String hostnameRemote = UA_STRING_NULL;
  167. UA_UInt16 portRemote = 4840;
  168. UA_String pathRemote = UA_STRING_NULL;
  169. UA_StatusCode retval =
  170. UA_parseEndpointUrl(&entry->serverOnNetwork.discoveryUrl,
  171. &hostnameRemote, &portRemote, &pathRemote);
  172. if(retval != UA_STATUSCODE_GOOD) {
  173. /* skip invalid url */
  174. return false;
  175. }
  176. #ifdef _WIN32
  177. IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
  178. if(!adapter_addresses) {
  179. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  180. "getifaddrs returned an unexpected error. Not setting mDNS A records.");
  181. return false;
  182. }
  183. #elif defined(UA_HAS_GETIFADDR)
  184. struct ifaddrs *ifaddr, *ifa;
  185. if(getifaddrs(&ifaddr) == -1) {
  186. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  187. "getifaddrs returned an unexpected error. Not setting mDNS A records.");
  188. return false;
  189. }
  190. #else
  191. if (server->config.discovery.ipAddressListSize == 0) {
  192. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  193. "If UA_HAS_GETIFADDR is false, config.discovery.ipAddressList must be set");
  194. return false;
  195. }
  196. #endif
  197. UA_Boolean isSelf = false;
  198. for (size_t i=0; i<server->config.networkLayersSize; i++) {
  199. UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
  200. UA_String hostnameSelf = UA_STRING_NULL;
  201. UA_UInt16 portSelf = 4840;
  202. UA_String pathSelf = UA_STRING_NULL;
  203. retval = UA_parseEndpointUrl(&nl->discoveryUrl, &hostnameSelf,
  204. &portSelf, &pathSelf);
  205. if(retval != UA_STATUSCODE_GOOD) {
  206. /* skip invalid url */
  207. continue;
  208. }
  209. if (portRemote != portSelf)
  210. continue;
  211. #ifdef _WIN32
  212. /* Iterate through all of the adapters */
  213. IP_ADAPTER_ADDRESSES* adapter = adapter_addresses;
  214. for(; adapter != NULL; adapter = adapter->Next) {
  215. /* Skip loopback adapters */
  216. if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
  217. continue;
  218. /* Parse all IPv4 and IPv6 addresses */
  219. IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
  220. for(; NULL != address; address = address->Next) {
  221. int family = address->Address.lpSockaddr->sa_family;
  222. if(AF_INET == family) {
  223. SOCKADDR_IN* ipv4 = (SOCKADDR_IN*)(address->Address.lpSockaddr); /* IPv4 */
  224. char *ipStr = inet_ntoa(ipv4->sin_addr);
  225. if(strncmp((const char*)hostnameRemote.data, ipStr,
  226. hostnameRemote.length) == 0) {
  227. isSelf = true;
  228. break;
  229. }
  230. } else if(AF_INET6 == family) {
  231. /* IPv6 not implemented yet */
  232. }
  233. }
  234. if (isSelf)
  235. break;
  236. }
  237. #elif defined(UA_HAS_GETIFADDR)
  238. /* Walk through linked list, maintaining head pointer so we can free
  239. * list later */
  240. int n;
  241. for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
  242. if(!ifa->ifa_addr)
  243. continue;
  244. if((strcmp("lo", ifa->ifa_name) == 0) ||
  245. !(ifa->ifa_flags & (IFF_RUNNING))||
  246. !(ifa->ifa_flags & (IFF_MULTICAST)))
  247. continue;
  248. /* IPv4 */
  249. if(ifa->ifa_addr->sa_family == AF_INET) {
  250. struct sockaddr_in* sa = (struct sockaddr_in*) ifa->ifa_addr;
  251. char *ipStr = inet_ntoa(sa->sin_addr);
  252. if(strncmp((const char*)hostnameRemote.data, ipStr,
  253. hostnameRemote.length) == 0) {
  254. isSelf = true;
  255. break;
  256. }
  257. }
  258. /* IPv6 not implemented yet */
  259. }
  260. #else
  261. for(size_t idx=0; i<server->config.discovery.ipAddressListSize; idx++) {
  262. char *ipStr = inet_ntoa(*( (struct in_addr *) &server->config.discovery.ipAddressList[idx]));
  263. if(strncmp((const char*)hostnameRemote.data, ipStr,
  264. hostnameRemote.length) == 0) {
  265. isSelf = true;
  266. break;
  267. }
  268. }
  269. #endif
  270. if (isSelf)
  271. break;
  272. }
  273. #ifdef _WIN32
  274. /* Cleanup */
  275. UA_free(adapter_addresses);
  276. adapter_addresses = NULL;
  277. #elif defined(UA_HAS_GETIFADDR)
  278. /* Clean up */
  279. freeifaddrs(ifaddr);
  280. #endif
  281. return isSelf;
  282. }
  283. static void
  284. mdns_record_remove(UA_Server *server, const char *record,
  285. struct serverOnNetwork_list_entry *entry) {
  286. UA_DiscoveryManager *dm = &server->discoveryManager;
  287. /* remove from hash */
  288. int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
  289. struct serverOnNetwork_hash_entry *hash_entry = dm->serverOnNetworkHash[hashIdx];
  290. struct serverOnNetwork_hash_entry *prevEntry = hash_entry;
  291. while(hash_entry) {
  292. if(hash_entry->entry == entry) {
  293. if(dm->serverOnNetworkHash[hashIdx] == hash_entry)
  294. dm->serverOnNetworkHash[hashIdx] = hash_entry->next;
  295. else if(prevEntry)
  296. prevEntry->next = hash_entry->next;
  297. break;
  298. }
  299. prevEntry = hash_entry;
  300. hash_entry = hash_entry->next;
  301. }
  302. UA_free(hash_entry);
  303. if(dm->serverOnNetworkCallback && !mdns_is_self_announce(server, entry))
  304. dm->serverOnNetworkCallback(&entry->serverOnNetwork, false,
  305. entry->txtSet, dm->serverOnNetworkCallbackData);
  306. /* remove from list */
  307. LIST_REMOVE(entry, pointers);
  308. UA_ServerOnNetwork_deleteMembers(&entry->serverOnNetwork);
  309. if(entry->pathTmp)
  310. UA_free(entry->pathTmp);
  311. #if UA_MULTITHREADING >= 200
  312. UA_atomic_subSize(&dm->serverOnNetworkSize, 1);
  313. entry->delayedCleanup.callback = NULL; /* Only free the structure */
  314. UA_WorkQueue_enqueueDelayed(&server->workQueue, &entry->delayedCleanup);
  315. #else
  316. dm->serverOnNetworkSize--;
  317. UA_free(entry);
  318. #endif
  319. }
  320. static void
  321. mdns_append_path_to_url(UA_String *url, const char *path) {
  322. size_t pathLen = strlen(path);
  323. /* todo: malloc may fail: return a statuscode */
  324. char *newUrl = (char *)UA_malloc(url->length + pathLen);
  325. memcpy(newUrl, url->data, url->length);
  326. memcpy(newUrl + url->length, path, pathLen);
  327. url->length = url->length + pathLen;
  328. url->data = (UA_Byte *) newUrl;
  329. }
  330. static void
  331. setTxt(UA_Server *server, const struct resource *r,
  332. struct serverOnNetwork_list_entry *entry) {
  333. entry->txtSet = true;
  334. xht_t *x = txt2sd(r->rdata, r->rdlength);
  335. char *path = (char *) xht_get(x, "path");
  336. char *caps = (char *) xht_get(x, "caps");
  337. size_t pathLen = path ? strlen(path) : 0;
  338. if(path && pathLen > 1) {
  339. if(!entry->srvSet) {
  340. /* txt arrived before SRV, thus cache path entry */
  341. if (!entry->pathTmp) {
  342. entry->pathTmp = (char*)UA_malloc(pathLen+1);
  343. if (!entry->pathTmp) {
  344. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot alloc memory for mDNS srv path");
  345. return;
  346. }
  347. memcpy(&(entry->pathTmp), &path, pathLen);
  348. entry->pathTmp[pathLen] = '\0';
  349. }
  350. } else {
  351. /* SRV already there and discovery URL set. Add path to discovery URL */
  352. mdns_append_path_to_url(&entry->serverOnNetwork.discoveryUrl, path);
  353. }
  354. }
  355. if(caps && strlen(caps) > 0) {
  356. /* count comma in caps */
  357. size_t capsCount = 1;
  358. for(size_t i = 0; caps[i]; i++) {
  359. if(caps[i] == ',')
  360. capsCount++;
  361. }
  362. /* set capabilities */
  363. entry->serverOnNetwork.serverCapabilitiesSize = capsCount;
  364. entry->serverOnNetwork.serverCapabilities =
  365. (UA_String *) UA_Array_new(capsCount, &UA_TYPES[UA_TYPES_STRING]);
  366. for(size_t i = 0; i < capsCount; i++) {
  367. char *nextStr = strchr(caps, ',');
  368. size_t len = nextStr ? (size_t) (nextStr - caps) : strlen(caps);
  369. entry->serverOnNetwork.serverCapabilities[i].length = len;
  370. /* todo: malloc may fail: return a statuscode */
  371. entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
  372. memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
  373. if(nextStr)
  374. caps = nextStr + 1;
  375. else
  376. break;
  377. }
  378. }
  379. xht_free(x);
  380. }
  381. /* [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname]. */
  382. static void
  383. setSrv(UA_Server *server, const struct resource *r,
  384. struct serverOnNetwork_list_entry *entry) {
  385. entry->srvSet = true;
  386. /* The specification Part 12 says: The hostname maps onto the SRV record
  387. * target field. If the hostname is an IPAddress then it must be converted
  388. * to a domain name. If this cannot be done then LDS shall report an
  389. * error. */
  390. size_t srvNameLen = strlen(r->known.srv.name);
  391. if(srvNameLen > 0 && r->known.srv.name[srvNameLen - 1] == '.')
  392. /* cut off last dot */
  393. srvNameLen--;
  394. /* opc.tcp://[servername]:[port][path] */
  395. char *newUrl = (char*)UA_malloc(10 + srvNameLen + 8);
  396. if (!newUrl) {
  397. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot allocate char for discovery url. Out of memory.");
  398. return;
  399. }
  400. UA_snprintf(newUrl, 10 + srvNameLen + 8, "opc.tcp://%.*s:%d", (int) srvNameLen,
  401. r->known.srv.name, r->known.srv.port);
  402. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  403. "Multicast DNS: found server: %s", newUrl);
  404. entry->serverOnNetwork.discoveryUrl = UA_String_fromChars(newUrl);
  405. UA_free(newUrl);
  406. if(entry->pathTmp) {
  407. mdns_append_path_to_url(&entry->serverOnNetwork.discoveryUrl, entry->pathTmp);
  408. UA_free(entry->pathTmp);
  409. }
  410. }
  411. /* This will be called by the mDNS library on every record which is received */
  412. void
  413. mdns_record_received(const struct resource *r, void *data) {
  414. UA_Server *server = (UA_Server *) data;
  415. /* we only need SRV and TXT records */
  416. /* TODO: remove magic number */
  417. if((r->clazz != QCLASS_IN && r->clazz != QCLASS_IN + 32768) ||
  418. (r->type != QTYPE_SRV && r->type != QTYPE_TXT))
  419. return;
  420. /* we only handle '_opcua-tcp._tcp.' records */
  421. char *opcStr = strstr(r->name, "_opcua-tcp._tcp.");
  422. if(!opcStr)
  423. return;
  424. /* Compute the length of the servername */
  425. size_t servernameLen = (size_t) (opcStr - r->name);
  426. if(servernameLen == 0)
  427. return;
  428. servernameLen--; /* remove point */
  429. /* Get entry */
  430. struct serverOnNetwork_list_entry *entry =
  431. mdns_record_add_or_get(&server->discoveryManager, r->name, r->name,
  432. servernameLen, r->ttl > 0);
  433. if(!entry)
  434. return;
  435. /* Check that the ttl is positive */
  436. if(r->ttl == 0) {
  437. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  438. "Multicast DNS: remove server (TTL=0): %.*s",
  439. (int)entry->serverOnNetwork.discoveryUrl.length,
  440. entry->serverOnNetwork.discoveryUrl.data);
  441. mdns_record_remove(server, r->name, entry);
  442. return;
  443. }
  444. /* Update lastSeen */
  445. entry->lastSeen = UA_DateTime_nowMonotonic();
  446. /* TXT and SRV are already set */
  447. if(entry->txtSet && entry->srvSet) {
  448. // call callback for every mdns package we received.
  449. // This will also call the callback multiple times
  450. if (server->discoveryManager.serverOnNetworkCallback &&
  451. !mdns_is_self_announce(server, entry))
  452. server->discoveryManager.
  453. serverOnNetworkCallback(&entry->serverOnNetwork, true, entry->txtSet,
  454. server->discoveryManager.serverOnNetworkCallbackData);
  455. return;
  456. }
  457. /* Add the resources */
  458. if(r->type == QTYPE_TXT && !entry->txtSet)
  459. setTxt(server, r, entry);
  460. else if (r->type == QTYPE_SRV && !entry->srvSet)
  461. setSrv(server, r, entry);
  462. /* Call callback to announce a new server */
  463. if(entry->srvSet && server->discoveryManager.serverOnNetworkCallback &&
  464. !mdns_is_self_announce(server, entry))
  465. server->discoveryManager.
  466. serverOnNetworkCallback(&entry->serverOnNetwork, true, entry->txtSet,
  467. server->discoveryManager.serverOnNetworkCallbackData);
  468. }
  469. void
  470. mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const char *path,
  471. const UA_String *capabilites, const size_t capabilitiesSize,
  472. void (*conflict)(char *host, int type, void *arg)) {
  473. mdns_record_t *r = mdnsd_unique(server->discoveryManager.mdnsDaemon, fullServiceDomain,
  474. QTYPE_TXT, 600, conflict, server);
  475. xht_t *h = xht_new(11);
  476. char *allocPath = NULL;
  477. if(!path || strlen(path) == 0) {
  478. xht_set(h, "path", "/");
  479. } else {
  480. /* path does not contain slash, so add it here */
  481. size_t pathLen = strlen(path);
  482. if(path[0] == '/') {
  483. allocPath = (char*)UA_malloc(pathLen+1);
  484. if (!allocPath) {
  485. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot alloc memory for txt path");
  486. return;
  487. }
  488. memcpy(&allocPath, &path, pathLen);
  489. allocPath[pathLen] = '\0';
  490. } else {
  491. /* todo: malloc may fail: return a statuscode */
  492. allocPath = (char*)UA_malloc(pathLen + 2);
  493. allocPath[0] = '/';
  494. memcpy(allocPath + 1, path, pathLen);
  495. allocPath[pathLen + 1] = '\0';
  496. }
  497. xht_set(h, "path", allocPath);
  498. }
  499. /* calculate max string length: */
  500. size_t capsLen = 0;
  501. for(size_t i = 0; i < capabilitiesSize; i++) {
  502. /* add comma or last \0 */
  503. capsLen += capabilites[i].length + 1;
  504. }
  505. char *caps = NULL;
  506. if(capsLen) {
  507. /* freed when xht_free is called */
  508. /* todo: malloc may fail: return a statuscode */
  509. caps = (char*)UA_malloc(sizeof(char) * capsLen);
  510. size_t idx = 0;
  511. for(size_t i = 0; i < capabilitiesSize; i++) {
  512. memcpy(caps + idx, (const char *) capabilites[i].data, capabilites[i].length);
  513. idx += capabilites[i].length + 1;
  514. caps[idx - 1] = ',';
  515. }
  516. caps[idx - 1] = '\0';
  517. xht_set(h, "caps", caps);
  518. } else {
  519. xht_set(h, "caps", "NA");
  520. }
  521. int txtRecordLength;
  522. unsigned char *packet = sd2txt(h, &txtRecordLength);
  523. if(allocPath)
  524. UA_free(allocPath);
  525. if(caps)
  526. UA_free(caps);
  527. xht_free(h);
  528. mdnsd_set_raw(server->discoveryManager.mdnsDaemon, r, (char *) packet,
  529. (unsigned short) txtRecordLength);
  530. UA_free(packet);
  531. }
  532. mdns_record_t *
  533. mdns_find_record(mdns_daemon_t *mdnsDaemon, unsigned short type,
  534. const char *host, const char *rdname) {
  535. mdns_record_t *r = mdnsd_get_published(mdnsDaemon, host);
  536. if(!r)
  537. return NULL;
  538. /* search for the record with the correct ptr hostname */
  539. while(r) {
  540. const mdns_answer_t *data = mdnsd_record_data(r);
  541. if(data->type == type && strcmp(data->rdname, rdname) == 0)
  542. return r;
  543. r = mdnsd_record_next(r);
  544. }
  545. return NULL;
  546. }
  547. /* set record in the given interface */
  548. static void
  549. mdns_set_address_record_if(UA_DiscoveryManager *dm, const char *fullServiceDomain,
  550. const char *localDomain, char *addr, UA_UInt16 addr_len) {
  551. /* [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]. */
  552. mdns_record_t *r = mdnsd_shared(dm->mdnsDaemon, fullServiceDomain, QTYPE_A, 600);
  553. mdnsd_set_raw(dm->mdnsDaemon, r, addr, addr_len);
  554. /* [hostname]. A [ip]. */
  555. r = mdnsd_shared(dm->mdnsDaemon, localDomain, QTYPE_A, 600);
  556. mdnsd_set_raw(dm->mdnsDaemon, r, addr, addr_len);
  557. }
  558. /* Loop over network interfaces and run set_address_record on each */
  559. #ifdef _WIN32
  560. void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
  561. const char *localDomain) {
  562. IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
  563. if(!adapter_addresses)
  564. return;
  565. /* Iterate through all of the adapters */
  566. IP_ADAPTER_ADDRESSES* adapter = adapter_addresses;
  567. for(; adapter != NULL; adapter = adapter->Next) {
  568. /* Skip loopback adapters */
  569. if(IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
  570. continue;
  571. /* Parse all IPv4 and IPv6 addresses */
  572. IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
  573. for(; NULL != address; address = address->Next) {
  574. int family = address->Address.lpSockaddr->sa_family;
  575. if(AF_INET == family) {
  576. SOCKADDR_IN* ipv4 = (SOCKADDR_IN*)(address->Address.lpSockaddr); /* IPv4 */
  577. mdns_set_address_record_if(&server->discoveryManager, fullServiceDomain,
  578. localDomain, (char *)&ipv4->sin_addr, 4);
  579. } else if(AF_INET6 == family) {
  580. /* IPv6 */
  581. #if 0
  582. SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*)(address->Address.lpSockaddr);
  583. char str_buffer[INET6_ADDRSTRLEN] = {0};
  584. inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);
  585. std::string ipv6_str(str_buffer);
  586. /* Detect and skip non-external addresses */
  587. UA_Boolean is_link_local(false);
  588. UA_Boolean is_special_use(false);
  589. if(0 == ipv6_str.find("fe")) {
  590. char c = ipv6_str[2];
  591. if(c == '8' || c == '9' || c == 'a' || c == 'b')
  592. is_link_local = true;
  593. } else if (0 == ipv6_str.find("2001:0:")) {
  594. is_special_use = true;
  595. }
  596. if(!(is_link_local || is_special_use))
  597. ipAddrs.mIpv6.push_back(ipv6_str);
  598. #endif
  599. }
  600. }
  601. }
  602. /* Cleanup */
  603. UA_free(adapter_addresses);
  604. adapter_addresses = NULL;
  605. }
  606. #elif defined(UA_HAS_GETIFADDR)
  607. void
  608. mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
  609. const char *localDomain) {
  610. struct ifaddrs *ifaddr, *ifa;
  611. if(getifaddrs(&ifaddr) == -1) {
  612. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  613. "getifaddrs returned an unexpected error. Not setting mDNS A records.");
  614. return;
  615. }
  616. /* Walk through linked list, maintaining head pointer so we can free list later */
  617. int n;
  618. for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
  619. if(!ifa->ifa_addr)
  620. continue;
  621. if((strcmp("lo", ifa->ifa_name) == 0) ||
  622. !(ifa->ifa_flags & (IFF_RUNNING))||
  623. !(ifa->ifa_flags & (IFF_MULTICAST)))
  624. continue;
  625. /* IPv4 */
  626. if(ifa->ifa_addr->sa_family == AF_INET) {
  627. struct sockaddr_in* sa = (struct sockaddr_in*) ifa->ifa_addr;
  628. mdns_set_address_record_if(&server->discoveryManager, fullServiceDomain,
  629. localDomain, (char*)&sa->sin_addr.s_addr, 4);
  630. }
  631. /* IPv6 not implemented yet */
  632. }
  633. /* Clean up */
  634. freeifaddrs(ifaddr);
  635. }
  636. #else /* _WIN32 */
  637. void
  638. mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
  639. const char *localDomain) {
  640. if (server->config.discovery.ipAddressListSize == 0) {
  641. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
  642. "If UA_HAS_GETIFADDR is false, config.discovery.ipAddressList must be set");
  643. return;
  644. }
  645. for(size_t i=0; i<server->config.discovery.ipAddressListSize; i++) {
  646. mdns_set_address_record_if(&server->discoveryManager, fullServiceDomain,
  647. localDomain, (char*)&server->config.discovery.ipAddressList[i], 4);
  648. }
  649. }
  650. #endif /* _WIN32 */
  651. #endif /* UA_ENABLE_DISCOVERY_MULTICAST */