ua_mdns.c 28 KB

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