ua_services_discovery_multicast.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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. /* Enable POSIX features */
  5. #ifndef _XOPEN_SOURCE
  6. # define _XOPEN_SOURCE 600
  7. #endif
  8. #ifndef _DEFAULT_SOURCE
  9. # define _DEFAULT_SOURCE
  10. #endif
  11. /* On older systems we need to define _BSD_SOURCE.
  12. * _DEFAULT_SOURCE is an alias for that. */
  13. #ifndef _BSD_SOURCE
  14. # define _BSD_SOURCE
  15. #endif
  16. #include "ua_server_internal.h"
  17. #include "ua_services.h"
  18. #include "ua_mdns_internal.h"
  19. #if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST)
  20. #ifdef _MSC_VER
  21. # ifndef UNDER_CE
  22. # include <io.h> //access
  23. # define access _access
  24. # endif
  25. #else
  26. # include <unistd.h> //access
  27. #endif
  28. #include <fcntl.h>
  29. #include <errno.h>
  30. #ifdef _WIN32
  31. # define CLOSESOCKET(S) closesocket((SOCKET)S)
  32. # define errno__ WSAGetLastError()
  33. #else
  34. # define CLOSESOCKET(S) close(S)
  35. # define errno__ errno
  36. #endif
  37. #include "ua_log_socket_error.h"
  38. #ifdef UA_ENABLE_MULTITHREADING
  39. static void *
  40. multicastWorkerLoop(UA_Server *server) {
  41. struct timeval next_sleep = {.tv_sec = 0, .tv_usec = 0};
  42. volatile UA_Boolean *running = &server->mdnsRunning;
  43. fd_set fds;
  44. while(*running) {
  45. FD_ZERO(&fds);
  46. FD_SET(server->mdnsSocket, &fds);
  47. select(server->mdnsSocket + 1, &fds, 0, 0, &next_sleep);
  48. if(!*running)
  49. break;
  50. unsigned short retVal =
  51. mdnsd_step(server->mdnsDaemon, server->mdnsSocket,
  52. FD_ISSET(server->mdnsSocket, &fds), true, &next_sleep);
  53. if (retVal == 1) {
  54. UA_LOG_SOCKET_ERRNO_WRAP(
  55. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  56. "Multicast error: Can not read from socket. %s", errno_str));
  57. break;
  58. } else if (retVal == 2) {
  59. UA_LOG_SOCKET_ERRNO_WRAP(
  60. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  61. "Multicast error: Can not write to socket. %s", errno_str));
  62. break;
  63. }
  64. }
  65. return NULL;
  66. }
  67. static UA_StatusCode
  68. multicastListenStart(UA_Server* server) {
  69. int err = pthread_create(&server->mdnsThread, NULL,
  70. (void* (*)(void*))multicastWorkerLoop, server);
  71. if(err != 0) {
  72. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  73. "Multicast error: Can not create multicast thread.");
  74. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  75. }
  76. return UA_STATUSCODE_GOOD;
  77. }
  78. static UA_StatusCode
  79. multicastListenStop(UA_Server* server) {
  80. mdnsd_shutdown(server->mdnsDaemon);
  81. // wake up select
  82. write(server->mdnsSocket, "\0", 1);
  83. if(pthread_join(server->mdnsThread, NULL)) {
  84. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  85. "Multicast error: Can not stop thread.");
  86. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  87. }
  88. return UA_STATUSCODE_BADNOTIMPLEMENTED;
  89. }
  90. # endif /* UA_ENABLE_MULTITHREADING */
  91. static UA_StatusCode
  92. addMdnsRecordForNetworkLayer(UA_Server *server, const UA_String *appName,
  93. const UA_ServerNetworkLayer* nl) {
  94. UA_String hostname = UA_STRING_NULL;
  95. UA_UInt16 port = 4840;
  96. UA_String path = UA_STRING_NULL;
  97. UA_StatusCode retval = UA_parseEndpointUrl(&nl->discoveryUrl, &hostname,
  98. &port, &path);
  99. if(retval != UA_STATUSCODE_GOOD) {
  100. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
  101. "Server url is invalid: %.*s",
  102. (int)nl->discoveryUrl.length, nl->discoveryUrl.data);
  103. return retval;
  104. }
  105. UA_Discovery_addRecord(server, appName, &hostname, port,
  106. &path, UA_DISCOVERY_TCP, UA_TRUE,
  107. server->config.serverCapabilities,
  108. &server->config.serverCapabilitiesSize);
  109. return UA_STATUSCODE_GOOD;
  110. }
  111. void startMulticastDiscoveryServer(UA_Server *server) {
  112. UA_String *appName = &server->config.mdnsServerName;
  113. for(size_t i = 0; i < server->config.networkLayersSize; i++)
  114. addMdnsRecordForNetworkLayer(server, appName, &server->config.networkLayers[i]);
  115. /* find any other server on the net */
  116. UA_Discovery_multicastQuery(server);
  117. # ifdef UA_ENABLE_MULTITHREADING
  118. multicastListenStart(server);
  119. # endif
  120. }
  121. void stopMulticastDiscoveryServer(UA_Server *server) {
  122. char hostname[256];
  123. if(gethostname(hostname, 255) == 0) {
  124. UA_String hnString = UA_STRING(hostname);
  125. UA_Discovery_removeRecord(server, &server->config.mdnsServerName,
  126. &hnString, 4840, UA_TRUE);
  127. } else {
  128. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  129. "Could not get hostname for multicast discovery.");
  130. }
  131. # ifdef UA_ENABLE_MULTITHREADING
  132. multicastListenStop(server);
  133. # else
  134. // send out last package with TTL = 0
  135. iterateMulticastDiscoveryServer(server, NULL, UA_FALSE);
  136. # endif
  137. }
  138. /* All filter criteria must be fulfilled */
  139. static UA_Boolean
  140. filterServerRecord(size_t serverCapabilityFilterSize, UA_String *serverCapabilityFilter,
  141. serverOnNetwork_list_entry* current) {
  142. for(size_t i = 0; i < serverCapabilityFilterSize; i++) {
  143. for(size_t j = 0; j < current->serverOnNetwork.serverCapabilitiesSize; j++)
  144. if(!UA_String_equal(&serverCapabilityFilter[i],
  145. &current->serverOnNetwork.serverCapabilities[j]))
  146. return false;
  147. }
  148. return true;
  149. }
  150. void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
  151. const UA_FindServersOnNetworkRequest *request,
  152. UA_FindServersOnNetworkResponse *response) {
  153. /* Set LastCounterResetTime */
  154. UA_DateTime_copy(&server->serverOnNetworkRecordIdLastReset,
  155. &response->lastCounterResetTime);
  156. /* Compute the max number of records to return */
  157. UA_UInt32 recordCount = 0;
  158. if(request->startingRecordId < server->serverOnNetworkRecordIdCounter)
  159. recordCount = server->serverOnNetworkRecordIdCounter - request->startingRecordId;
  160. if(request->maxRecordsToReturn && recordCount > request->maxRecordsToReturn)
  161. recordCount = MIN(recordCount, request->maxRecordsToReturn);
  162. if(recordCount == 0) {
  163. response->serversSize = 0;
  164. return;
  165. }
  166. /* Iterate over all records and add to filtered list */
  167. UA_UInt32 filteredCount = 0;
  168. UA_ServerOnNetwork** filtered =
  169. (UA_ServerOnNetwork**)UA_alloca(sizeof(UA_ServerOnNetwork*) * recordCount);
  170. serverOnNetwork_list_entry* current;
  171. LIST_FOREACH(current, &server->serverOnNetwork, pointers) {
  172. if(filteredCount >= recordCount)
  173. break;
  174. if(current->serverOnNetwork.recordId < request->startingRecordId)
  175. continue;
  176. if(!filterServerRecord(request->serverCapabilityFilterSize,
  177. request->serverCapabilityFilter, current))
  178. continue;
  179. filtered[filteredCount++] = &current->serverOnNetwork;
  180. }
  181. if(filteredCount == 0)
  182. return;
  183. /* Allocate the array for the response */
  184. response->servers =
  185. (UA_ServerOnNetwork*)UA_malloc(sizeof(UA_ServerOnNetwork)*filteredCount);
  186. if(!response->servers) {
  187. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  188. return;
  189. }
  190. response->serversSize = filteredCount;
  191. /* Copy the server names */
  192. for(size_t i = 0; i < filteredCount; i++)
  193. UA_ServerOnNetwork_copy(filtered[i], &response->servers[filteredCount-i-1]);
  194. }
  195. void
  196. UA_Discovery_update_MdnsForDiscoveryUrl(UA_Server *server, const UA_String *serverName,
  197. const UA_MdnsDiscoveryConfiguration *mdnsConfig,
  198. const UA_String *discoveryUrl,
  199. UA_Boolean isOnline, UA_Boolean updateTxt) {
  200. UA_String hostname = UA_STRING_NULL;
  201. UA_UInt16 port = 4840;
  202. UA_String path = UA_STRING_NULL;
  203. UA_StatusCode retval = UA_parseEndpointUrl(discoveryUrl, &hostname, &port, &path);
  204. if(retval != UA_STATUSCODE_GOOD) {
  205. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_NETWORK,
  206. "Server url invalid: %.*s",
  207. (int)discoveryUrl->length, discoveryUrl->data);
  208. return;
  209. }
  210. if(!isOnline) {
  211. UA_StatusCode removeRetval =
  212. UA_Discovery_removeRecord(server, serverName, &hostname,
  213. port, updateTxt);
  214. if(removeRetval != UA_STATUSCODE_GOOD)
  215. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  216. "Could not remove mDNS record for hostname %.*s.",
  217. (int)serverName->length, serverName->data);
  218. return;
  219. }
  220. UA_String *capabilities = NULL;
  221. size_t capabilitiesSize = 0;
  222. if(mdnsConfig) {
  223. capabilities = mdnsConfig->serverCapabilities;
  224. capabilitiesSize = mdnsConfig->serverCapabilitiesSize;
  225. }
  226. UA_StatusCode addRetval =
  227. UA_Discovery_addRecord(server, serverName, &hostname,
  228. port, &path, UA_DISCOVERY_TCP, updateTxt,
  229. capabilities, &capabilitiesSize);
  230. if(addRetval != UA_STATUSCODE_GOOD)
  231. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  232. "Could not add mDNS record for hostname %.*s.",
  233. (int)serverName->length, serverName->data);
  234. }
  235. void
  236. UA_Server_setServerOnNetworkCallback(UA_Server *server,
  237. UA_Server_serverOnNetworkCallback cb,
  238. void* data) {
  239. server->serverOnNetworkCallback = cb;
  240. server->serverOnNetworkCallbackData = data;
  241. }
  242. static void
  243. socket_mdns_set_nonblocking(int sockfd) {
  244. #ifdef _WIN32
  245. u_long iMode = 1;
  246. ioctlsocket(sockfd, FIONBIO, &iMode);
  247. #else
  248. int opts = fcntl(sockfd, F_GETFL);
  249. fcntl(sockfd, F_SETFL, opts|O_NONBLOCK);
  250. #endif
  251. }
  252. /* Create multicast 224.0.0.251:5353 socket */
  253. static int
  254. discovery_createMulticastSocket(void) {
  255. int s, flag = 1, ittl = 255;
  256. struct sockaddr_in in;
  257. struct ip_mreq mc;
  258. char ttl = (char)255; // publish to complete net, not only subnet. See:
  259. // https://docs.oracle.com/cd/E23824_01/html/821-1602/sockets-137.html
  260. memset(&in, 0, sizeof(in));
  261. in.sin_family = AF_INET;
  262. in.sin_port = htons(5353);
  263. in.sin_addr.s_addr = 0;
  264. if ((s = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
  265. return 0;
  266. #ifdef SO_REUSEPORT
  267. setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
  268. #endif
  269. setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
  270. if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
  271. CLOSESOCKET(s);
  272. return 0;
  273. }
  274. mc.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
  275. mc.imr_interface.s_addr = htonl(INADDR_ANY);
  276. setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mc, sizeof(mc));
  277. setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
  278. setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, sizeof(ittl));
  279. socket_mdns_set_nonblocking(s);
  280. return s;
  281. }
  282. UA_StatusCode
  283. initMulticastDiscoveryServer(UA_Server* server) {
  284. server->mdnsDaemon = mdnsd_new(QCLASS_IN, 1000);
  285. #ifdef _WIN32
  286. WORD wVersionRequested = MAKEWORD(2, 2);
  287. WSADATA wsaData;
  288. WSAStartup(wVersionRequested, &wsaData);
  289. #endif
  290. if((server->mdnsSocket = discovery_createMulticastSocket()) == 0) {
  291. UA_LOG_SOCKET_ERRNO_WRAP(
  292. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  293. "Could not create multicast socket. Error: %s", errno_str));
  294. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  295. }
  296. mdnsd_register_receive_callback(server->mdnsDaemon,
  297. mdns_record_received, server);
  298. return UA_STATUSCODE_GOOD;
  299. }
  300. void destroyMulticastDiscoveryServer(UA_Server* server) {
  301. mdnsd_shutdown(server->mdnsDaemon);
  302. mdnsd_free(server->mdnsDaemon);
  303. }
  304. static void
  305. UA_Discovery_multicastConflict(char *name, int type, void *arg) {
  306. // cppcheck-suppress unreadVariable
  307. UA_Server *server = (UA_Server*) arg;
  308. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  309. "Multicast DNS name conflict detected: "
  310. "'%s' for type %d", name, type);
  311. }
  312. /* Create a service domain with the format [servername]-[hostname]._opcua-tcp._tcp.local. */
  313. static void
  314. createFullServiceDomain(char *outServiceDomain, size_t maxLen,
  315. const UA_String *servername, const UA_String *hostname) {
  316. size_t hostnameLen = hostname->length;
  317. size_t servernameLen = servername->length;
  318. maxLen -= 24; /* the length we have remaining before the opc ua postfix and
  319. * the trailing zero */
  320. /* Can we use hostname and servername with full length? */
  321. if(hostnameLen + servernameLen + 1 > maxLen) {
  322. if(servernameLen + 2 > maxLen) {
  323. servernameLen = maxLen;
  324. hostnameLen = 0;
  325. } else {
  326. hostnameLen = maxLen - servernameLen - 1;
  327. }
  328. }
  329. /* Copy into outServiceDomain */
  330. size_t offset = 0;
  331. memcpy(&outServiceDomain[offset], servername->data, servernameLen);
  332. offset += servernameLen;
  333. if(hostnameLen > 0) {
  334. memcpy(&outServiceDomain[offset], "-", 1);
  335. ++offset;
  336. memcpy(&outServiceDomain[offset], hostname->data, hostnameLen);
  337. offset += hostnameLen;
  338. }
  339. memcpy(&outServiceDomain[offset], "._opcua-tcp._tcp.local.", 23);
  340. offset += 23;
  341. outServiceDomain[offset] = 0;
  342. }
  343. /* Check if mDNS already has an entry for given hostname and port combination */
  344. static UA_Boolean
  345. UA_Discovery_recordExists(UA_Server* server, const char* fullServiceDomain,
  346. unsigned short port, const UA_DiscoveryProtocol protocol) {
  347. // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname].
  348. mdns_record_t *r = mdnsd_get_published(server->mdnsDaemon, fullServiceDomain);
  349. while (r) {
  350. const mdns_answer_t *data = mdnsd_record_data(r);
  351. if (data->type == QTYPE_SRV && (port == 0 || data->srv.port == port))
  352. return UA_TRUE;
  353. r = mdnsd_record_next(r);
  354. }
  355. return UA_FALSE;
  356. }
  357. static int
  358. discovery_multicastQueryAnswer(mdns_answer_t *a, void *arg) {
  359. UA_Server *server = (UA_Server*) arg;
  360. if(a->type != QTYPE_PTR)
  361. return 0;
  362. if(a->rdname == NULL)
  363. return 0;
  364. /* Skip, if we already know about this server */
  365. UA_Boolean exists =
  366. UA_Discovery_recordExists(server, a->rdname, 0, UA_DISCOVERY_TCP);
  367. if(exists == UA_TRUE)
  368. return 0;
  369. if(mdnsd_has_query(server->mdnsDaemon, a->rdname))
  370. return 0;
  371. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  372. "mDNS send query for: %s SRV&TXT %s", a->name, a->rdname);
  373. mdnsd_query(server->mdnsDaemon, a->rdname, QTYPE_SRV,
  374. discovery_multicastQueryAnswer, server);
  375. mdnsd_query(server->mdnsDaemon, a->rdname, QTYPE_TXT,
  376. discovery_multicastQueryAnswer, server);
  377. return 0;
  378. }
  379. UA_StatusCode
  380. UA_Discovery_multicastQuery(UA_Server* server) {
  381. mdnsd_query(server->mdnsDaemon, "_opcua-tcp._tcp.local.",
  382. QTYPE_PTR,discovery_multicastQueryAnswer, server);
  383. return UA_STATUSCODE_GOOD;
  384. }
  385. UA_StatusCode
  386. UA_Discovery_addRecord(UA_Server *server, const UA_String *servername,
  387. const UA_String *hostname, UA_UInt16 port,
  388. const UA_String *path, const UA_DiscoveryProtocol protocol,
  389. UA_Boolean createTxt, const UA_String* capabilites,
  390. size_t *capabilitiesSize) {
  391. if(!capabilitiesSize || (*capabilitiesSize > 0 && !capabilites))
  392. return UA_STATUSCODE_BADINVALIDARGUMENT;
  393. size_t hostnameLen = hostname->length;
  394. size_t servernameLen = servername->length;
  395. if(hostnameLen == 0 || servernameLen == 0)
  396. return UA_STATUSCODE_BADOUTOFRANGE;
  397. // use a limit for the hostname length to make sure full string fits into 63
  398. // chars (limited by DNS spec)
  399. if(hostnameLen+servernameLen + 1 > 63) { // include dash between servername-hostname
  400. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  401. "Multicast DNS: Combination of hostname+servername exceeds "
  402. "maximum of 62 chars. It will be truncated.");
  403. } else if(hostnameLen > 63) {
  404. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  405. "Multicast DNS: Hostname length exceeds maximum of 63 chars. "
  406. "It will be truncated.");
  407. }
  408. if(!server->mdnsMainSrvAdded) {
  409. mdns_record_t *r =
  410. mdnsd_shared(server->mdnsDaemon, "_services._dns-sd._udp.local.",
  411. QTYPE_PTR, 600);
  412. mdnsd_set_host(server->mdnsDaemon, r, "_opcua-tcp._tcp.local.");
  413. server->mdnsMainSrvAdded = UA_TRUE;
  414. }
  415. // [servername]-[hostname]._opcua-tcp._tcp.local.
  416. char fullServiceDomain[63+24];
  417. createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname);
  418. UA_Boolean exists = UA_Discovery_recordExists(server, fullServiceDomain, port, protocol);
  419. if(exists == UA_TRUE)
  420. return UA_STATUSCODE_GOOD;
  421. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  422. "Multicast DNS: add record for domain: %s", fullServiceDomain);
  423. // _services._dns-sd._udp.local. PTR _opcua-tcp._tcp.local
  424. // check if there is already a PTR entry for the given service.
  425. // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local.
  426. mdns_record_t *r = mdns_find_record(server->mdnsDaemon, QTYPE_PTR,
  427. "_opcua-tcp._tcp.local.", fullServiceDomain);
  428. if(!r) {
  429. r = mdnsd_shared(server->mdnsDaemon, "_opcua-tcp._tcp.local.", QTYPE_PTR, 600);
  430. mdnsd_set_host(server->mdnsDaemon, r, fullServiceDomain);
  431. }
  432. /* The first 63 characters of the hostname (or less) */
  433. size_t maxHostnameLen = MIN(hostnameLen, 63);
  434. char localDomain[65];
  435. memcpy(localDomain, hostname->data, maxHostnameLen);
  436. localDomain[maxHostnameLen] = '.';
  437. localDomain[maxHostnameLen+1] = '\0';
  438. // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname].
  439. r = mdnsd_unique(server->mdnsDaemon, fullServiceDomain, QTYPE_SRV, 600,
  440. UA_Discovery_multicastConflict, server);
  441. mdnsd_set_srv(server->mdnsDaemon, r, 0, 0, port, localDomain);
  442. // A/AAAA record for all ip addresses.
  443. // [servername]-[hostname]._opcua-tcp._tcp.local. A [ip].
  444. // [hostname]. A [ip].
  445. mdns_set_address_record(server, fullServiceDomain, localDomain);
  446. // TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,...
  447. if(createTxt) {
  448. char *pathChars = (char *)UA_alloca(path->length + 1);
  449. memcpy(pathChars, path->data, path->length);
  450. pathChars[path->length] = 0;
  451. mdns_create_txt(server, fullServiceDomain, pathChars, capabilites,
  452. capabilitiesSize, UA_Discovery_multicastConflict);
  453. }
  454. return UA_STATUSCODE_GOOD;
  455. }
  456. UA_StatusCode
  457. UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
  458. const UA_String *hostname, UA_UInt16 port,
  459. UA_Boolean removeTxt) {
  460. // use a limit for the hostname length to make sure full string fits into 63
  461. // chars (limited by DNS spec)
  462. size_t hostnameLen = hostname->length;
  463. size_t servernameLen = servername->length;
  464. if(hostnameLen == 0 || servernameLen == 0)
  465. return UA_STATUSCODE_BADOUTOFRANGE;
  466. if(hostnameLen+servernameLen+1 > 63) { // include dash between servername-hostname
  467. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  468. "Multicast DNS: Combination of hostname+servername exceeds "
  469. "maximum of 62 chars. It will be truncated.");
  470. }
  471. // [servername]-[hostname]._opcua-tcp._tcp.local.
  472. char fullServiceDomain[63 + 24];
  473. createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname);
  474. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  475. "Multicast DNS: remove record for domain: %s", fullServiceDomain);
  476. // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local.
  477. mdns_record_t *r = mdns_find_record(server->mdnsDaemon, QTYPE_PTR,
  478. "_opcua-tcp._tcp.local.", fullServiceDomain);
  479. if(!r) {
  480. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  481. "Multicast DNS: could not remove record. "
  482. "PTR Record not found for domain: %s", fullServiceDomain);
  483. return UA_STATUSCODE_BADNOTHINGTODO;
  484. }
  485. mdnsd_done(server->mdnsDaemon, r);
  486. // looks for [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port hostname.local.
  487. // and TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,...
  488. // and A record: [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]
  489. mdns_record_t *r2 = mdnsd_get_published(server->mdnsDaemon, fullServiceDomain);
  490. if(!r2) {
  491. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  492. "Multicast DNS: could not remove record. Record not "
  493. "found for domain: %s", fullServiceDomain);
  494. return UA_STATUSCODE_BADNOTHINGTODO;
  495. }
  496. while(r2) {
  497. const mdns_answer_t *data = mdnsd_record_data(r2);
  498. mdns_record_t *next = mdnsd_record_next(r2);
  499. if((removeTxt && data->type == QTYPE_TXT) ||
  500. (removeTxt && data->type == QTYPE_A) ||
  501. data->srv.port == port) {
  502. mdnsd_done(server->mdnsDaemon, r2);
  503. }
  504. r2 = next;
  505. }
  506. return UA_STATUSCODE_GOOD;
  507. }
  508. UA_StatusCode
  509. iterateMulticastDiscoveryServer(UA_Server* server, UA_DateTime *nextRepeat,
  510. UA_Boolean processIn) {
  511. struct timeval next_sleep = { 0, 0 };
  512. unsigned short retval = mdnsd_step(server->mdnsDaemon, server->mdnsSocket,
  513. processIn, true, &next_sleep);
  514. if(retval == 1) {
  515. UA_LOG_SOCKET_ERRNO_WRAP(
  516. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  517. "Multicast error: Can not read from socket. %s", errno_str));
  518. return UA_STATUSCODE_BADNOCOMMUNICATION;
  519. } else if(retval == 2) {
  520. UA_LOG_SOCKET_ERRNO_WRAP(
  521. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  522. "Multicast error: Can not write to socket. %s", errno_str));
  523. return UA_STATUSCODE_BADNOCOMMUNICATION;
  524. }
  525. if(nextRepeat)
  526. *nextRepeat = UA_DateTime_now() +
  527. (UA_DateTime)(next_sleep.tv_sec * UA_SEC_TO_DATETIME +
  528. next_sleep.tv_usec * UA_USEC_TO_DATETIME);
  529. return UA_STATUSCODE_GOOD;
  530. }
  531. #endif /* defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST) */