ua_mdns.c 29 KB

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