ua_services_discovery_multicast.c 24 KB

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