ua_services_discovery_multicast.c 22 KB

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