ua_services_discovery_multicast.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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. 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. 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. serverName);
  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.", serverName);
  218. }
  219. void
  220. UA_Server_setServerOnNetworkCallback(UA_Server *server,
  221. UA_Server_serverOnNetworkCallback cb,
  222. void* data) {
  223. server->serverOnNetworkCallback = cb;
  224. server->serverOnNetworkCallbackData = data;
  225. }
  226. static void
  227. socket_mdns_set_nonblocking(int sockfd) {
  228. #ifdef _WIN32
  229. u_long iMode = 1;
  230. ioctlsocket(sockfd, FIONBIO, &iMode);
  231. #else
  232. int opts = fcntl(sockfd, F_GETFL);
  233. fcntl(sockfd, F_SETFL, opts|O_NONBLOCK);
  234. #endif
  235. }
  236. /* Create multicast 224.0.0.251:5353 socket */
  237. static int
  238. discovery_createMulticastSocket(void) {
  239. int s, flag = 1, ittl = 255;
  240. struct sockaddr_in in;
  241. struct ip_mreq mc;
  242. char ttl = (char)255; // publish to complete net, not only subnet. See:
  243. // https://docs.oracle.com/cd/E23824_01/html/821-1602/sockets-137.html
  244. memset(&in, 0, sizeof(in));
  245. in.sin_family = AF_INET;
  246. in.sin_port = htons(5353);
  247. in.sin_addr.s_addr = 0;
  248. if ((s = (int)socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  249. return 0;
  250. #ifdef SO_REUSEPORT
  251. setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
  252. #endif
  253. setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
  254. if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
  255. CLOSESOCKET(s);
  256. return 0;
  257. }
  258. mc.imr_multiaddr.s_addr = inet_addr("224.0.0.251");
  259. mc.imr_interface.s_addr = htonl(INADDR_ANY);
  260. setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mc, sizeof(mc));
  261. setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
  262. setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, sizeof(ittl));
  263. socket_mdns_set_nonblocking(s);
  264. return s;
  265. }
  266. UA_StatusCode
  267. initMulticastDiscoveryServer(UA_Server* server) {
  268. server->mdnsDaemon = mdnsd_new(QCLASS_IN, 1000);
  269. if((server->mdnsSocket = discovery_createMulticastSocket()) == 0) {
  270. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  271. "Could not create multicast socket. Error: %s",
  272. strerror(errno));
  273. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  274. }
  275. mdnsd_register_receive_callback(server->mdnsDaemon,
  276. mdns_record_received, server);
  277. return UA_STATUSCODE_GOOD;
  278. }
  279. void destroyMulticastDiscoveryServer(UA_Server* server) {
  280. mdnsd_shutdown(server->mdnsDaemon);
  281. mdnsd_free(server->mdnsDaemon);
  282. }
  283. static void
  284. UA_Discovery_multicastConflict(char *name, int type, void *arg) {
  285. // cppcheck-suppress unreadVariable
  286. UA_Server *server = (UA_Server*) arg;
  287. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  288. "Multicast DNS name conflict detected: "
  289. "'%s' for type %d", name, type);
  290. }
  291. /* Create a service domain with the format [servername]-[hostname]._opcua-tcp._tcp.local. */
  292. static void
  293. createFullServiceDomain(char *outServiceDomain, size_t maxLen,
  294. const UA_String *servername, const UA_String *hostname) {
  295. size_t hostnameLen = hostname->length;
  296. size_t servernameLen = servername->length;
  297. maxLen -= 24; /* the length we have remaining before the opc ua postfix and
  298. * the trailing zero */
  299. /* Can we use hostname and servername with full length? */
  300. if(hostnameLen + servernameLen + 1 > maxLen) {
  301. if(servernameLen + 2 > maxLen) {
  302. servernameLen = maxLen;
  303. hostnameLen = 0;
  304. } else {
  305. hostnameLen = maxLen - servernameLen - 1;
  306. }
  307. }
  308. /* Copy into outServiceDomain */
  309. size_t pos = 0;
  310. memcpy(&outServiceDomain[pos], servername->data, servernameLen);
  311. pos += servernameLen;
  312. if(hostnameLen > 0) {
  313. memcpy(&outServiceDomain[pos], "-", 1);
  314. ++pos;
  315. memcpy(&outServiceDomain[pos], hostname->data, hostnameLen);
  316. pos += hostnameLen;
  317. }
  318. memcpy(&outServiceDomain[pos], "._opcua-tcp._tcp.local.", 23);
  319. pos += 23;
  320. outServiceDomain[pos] = 0;
  321. }
  322. /* Check if mDNS already has an entry for given hostname and port combination */
  323. static UA_Boolean
  324. UA_Discovery_recordExists(UA_Server* server, const char* fullServiceDomain,
  325. unsigned short port, const UA_DiscoveryProtocol protocol) {
  326. // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname].
  327. mdns_record_t *r = mdnsd_get_published(server->mdnsDaemon, fullServiceDomain);
  328. while (r) {
  329. const mdns_answer_t *data = mdnsd_record_data(r);
  330. if (data->type == QTYPE_SRV && (port == 0 || data->srv.port == port))
  331. return UA_TRUE;
  332. r = mdnsd_record_next(r);
  333. }
  334. return UA_FALSE;
  335. }
  336. static int
  337. discovery_multicastQueryAnswer(mdns_answer_t *a, void *arg) {
  338. UA_Server *server = (UA_Server*) arg;
  339. if(a->type != QTYPE_PTR)
  340. return 0;
  341. if(a->rdname == NULL)
  342. return 0;
  343. /* Skip, if we already know about this server */
  344. UA_Boolean exists =
  345. UA_Discovery_recordExists(server, a->rdname, 0, UA_DISCOVERY_TCP);
  346. if(exists == UA_TRUE)
  347. return 0;
  348. if(mdnsd_has_query(server->mdnsDaemon, a->rdname))
  349. return 0;
  350. UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
  351. "mDNS send query for: %s SRV&TXT %s", a->name, a->rdname);
  352. mdnsd_query(server->mdnsDaemon, a->rdname, QTYPE_SRV,
  353. discovery_multicastQueryAnswer, server);
  354. mdnsd_query(server->mdnsDaemon, a->rdname, QTYPE_TXT,
  355. discovery_multicastQueryAnswer, server);
  356. return 0;
  357. }
  358. UA_StatusCode
  359. UA_Discovery_multicastQuery(UA_Server* server) {
  360. mdnsd_query(server->mdnsDaemon, "_opcua-tcp._tcp.local.",
  361. QTYPE_PTR,discovery_multicastQueryAnswer, server);
  362. return UA_STATUSCODE_GOOD;
  363. }
  364. UA_StatusCode
  365. UA_Discovery_addRecord(UA_Server *server, const UA_String *servername,
  366. const UA_String *hostname, UA_UInt16 port,
  367. const UA_String *path, const UA_DiscoveryProtocol protocol,
  368. UA_Boolean createTxt, const UA_String* capabilites,
  369. size_t *capabilitiesSize) {
  370. if(!capabilitiesSize || (*capabilitiesSize > 0 && !capabilites))
  371. return UA_STATUSCODE_BADINVALIDARGUMENT;
  372. size_t hostnameLen = hostname->length;
  373. size_t servernameLen = servername->length;
  374. if(hostnameLen == 0 || servernameLen == 0)
  375. return UA_STATUSCODE_BADOUTOFRANGE;
  376. // use a limit for the hostname length to make sure full string fits into 63
  377. // chars (limited by DNS spec)
  378. if(hostnameLen+servernameLen + 1 > 63) { // include dash between servername-hostname
  379. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  380. "Multicast DNS: Combination of hostname+servername exceeds "
  381. "maximum of 62 chars. It will be truncated.");
  382. } else if(hostnameLen > 63) {
  383. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  384. "Multicast DNS: Hostname length exceeds maximum of 63 chars. "
  385. "It will be truncated.");
  386. }
  387. if(!server->mdnsMainSrvAdded) {
  388. mdns_record_t *r =
  389. mdnsd_shared(server->mdnsDaemon, "_services._dns-sd._udp.local.",
  390. QTYPE_PTR, 600);
  391. mdnsd_set_host(server->mdnsDaemon, r, "_opcua-tcp._tcp.local.");
  392. server->mdnsMainSrvAdded = UA_TRUE;
  393. }
  394. // [servername]-[hostname]._opcua-tcp._tcp.local.
  395. char fullServiceDomain[63+24];
  396. createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname);
  397. UA_Boolean exists = UA_Discovery_recordExists(server, fullServiceDomain, port, protocol);
  398. if(exists == UA_TRUE)
  399. return UA_STATUSCODE_GOOD;
  400. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  401. "Multicast DNS: add record for domain: %s", fullServiceDomain);
  402. // _services._dns-sd._udp.local. PTR _opcua-tcp._tcp.local
  403. // check if there is already a PTR entry for the given service.
  404. // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local.
  405. mdns_record_t *r = mdns_find_record(server->mdnsDaemon, QTYPE_PTR,
  406. "_opcua-tcp._tcp.local.", fullServiceDomain);
  407. if(!r) {
  408. r = mdnsd_shared(server->mdnsDaemon, "_opcua-tcp._tcp.local.", QTYPE_PTR, 600);
  409. mdnsd_set_host(server->mdnsDaemon, r, fullServiceDomain);
  410. }
  411. /* The first 63 characters of the hostname (or less) */
  412. size_t maxHostnameLen = MIN(hostnameLen, 63);
  413. char localDomain[65];
  414. memcpy(localDomain, hostname->data, maxHostnameLen);
  415. localDomain[maxHostnameLen] = '.';
  416. localDomain[maxHostnameLen+1] = '\0';
  417. // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname].
  418. r = mdnsd_unique(server->mdnsDaemon, fullServiceDomain, QTYPE_SRV, 600,
  419. UA_Discovery_multicastConflict, server);
  420. mdnsd_set_srv(server->mdnsDaemon, r, 0, 0, port, localDomain);
  421. // A/AAAA record for all ip addresses.
  422. // [servername]-[hostname]._opcua-tcp._tcp.local. A [ip].
  423. // [hostname]. A [ip].
  424. mdns_set_address_record(server, fullServiceDomain, localDomain);
  425. // TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,...
  426. if(createTxt) {
  427. char *pathChars = (char *)UA_alloca(path->length + 1);
  428. memcpy(pathChars, path->data, path->length);
  429. pathChars[path->length] = 0;
  430. mdns_create_txt(server, fullServiceDomain, pathChars, capabilites,
  431. capabilitiesSize, UA_Discovery_multicastConflict);
  432. }
  433. return UA_STATUSCODE_GOOD;
  434. }
  435. UA_StatusCode
  436. UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
  437. const UA_String *hostname, UA_UInt16 port,
  438. UA_Boolean removeTxt) {
  439. // use a limit for the hostname length to make sure full string fits into 63
  440. // chars (limited by DNS spec)
  441. size_t hostnameLen = hostname->length;
  442. size_t servernameLen = servername->length;
  443. if(hostnameLen == 0 || servernameLen == 0)
  444. return UA_STATUSCODE_BADOUTOFRANGE;
  445. if(hostnameLen+servernameLen+1 > 63) { // include dash between servername-hostname
  446. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  447. "Multicast DNS: Combination of hostname+servername exceeds "
  448. "maximum of 62 chars. It will be truncated.");
  449. }
  450. // [servername]-[hostname]._opcua-tcp._tcp.local.
  451. char fullServiceDomain[63 + 24];
  452. createFullServiceDomain(fullServiceDomain, 63+24, servername, hostname);
  453. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
  454. "Multicast DNS: remove record for domain: %s", fullServiceDomain);
  455. // _opcua-tcp._tcp.local. PTR [servername]-[hostname]._opcua-tcp._tcp.local.
  456. mdns_record_t *r = mdns_find_record(server->mdnsDaemon, QTYPE_PTR,
  457. "_opcua-tcp._tcp.local.", fullServiceDomain);
  458. if(!r) {
  459. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  460. "Multicast DNS: could not remove record. "
  461. "PTR Record not found for domain: %s", fullServiceDomain);
  462. return UA_STATUSCODE_BADNOTFOUND;
  463. }
  464. mdnsd_done(server->mdnsDaemon, r);
  465. // looks for [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port hostname.local.
  466. // and TXT record: [servername]-[hostname]._opcua-tcp._tcp.local. TXT path=/ caps=NA,DA,...
  467. // and A record: [servername]-[hostname]._opcua-tcp._tcp.local. A [ip]
  468. mdns_record_t *r2 = mdnsd_get_published(server->mdnsDaemon, fullServiceDomain);
  469. if(!r2) {
  470. UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
  471. "Multicast DNS: could not remove record. Record not "
  472. "found for domain: %s", fullServiceDomain);
  473. return UA_STATUSCODE_BADNOTFOUND;
  474. }
  475. while(r2) {
  476. const mdns_answer_t *data = mdnsd_record_data(r2);
  477. mdns_record_t *next = mdnsd_record_next(r2);
  478. if((removeTxt && data->type == QTYPE_TXT) ||
  479. (removeTxt && data->type == QTYPE_A) ||
  480. data->srv.port == port) {
  481. mdnsd_done(server->mdnsDaemon, r2);
  482. }
  483. r2 = next;
  484. }
  485. return UA_STATUSCODE_GOOD;
  486. }
  487. UA_StatusCode
  488. iterateMulticastDiscoveryServer(UA_Server* server, UA_DateTime *nextRepeat,
  489. UA_Boolean processIn) {
  490. struct timeval next_sleep = { 0, 0 };
  491. unsigned short retval = mdnsd_step(server->mdnsDaemon, server->mdnsSocket,
  492. processIn, true, &next_sleep);
  493. if(retval == 1) {
  494. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  495. "Multicast error: Can not read from socket. %s",
  496. strerror(errno));
  497. return UA_STATUSCODE_BADNOCOMMUNICATION;
  498. } else if(retval == 2) {
  499. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
  500. "Multicast error: Can not write to socket. %s",
  501. strerror(errno));
  502. return UA_STATUSCODE_BADNOCOMMUNICATION;
  503. }
  504. if(nextRepeat)
  505. *nextRepeat = UA_DateTime_now() +
  506. (UA_DateTime)(next_sleep.tv_sec * UA_SEC_TO_DATETIME +
  507. next_sleep.tv_usec * UA_USEC_TO_DATETIME);
  508. return UA_STATUSCODE_GOOD;
  509. }
  510. #endif /* defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST) */