ua_services_discovery_multicast.c 23 KB

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