/src/open62541/arch/posix/eventloop_posix_udp.c
Line | Count | Source |
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 | | * |
5 | | * Copyright 2021-2022 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes) |
7 | | */ |
8 | | |
9 | | #include "eventloop_posix.h" |
10 | | |
11 | | #if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) || defined(UA_ARCHITECTURE_WIN32) |
12 | | |
13 | 568 | #define IPV4_PREFIX_MASK 0xF0 |
14 | 568 | #define IPV4_MULTICAST_PREFIX 0xE0 |
15 | | #if UA_IPV6 |
16 | 0 | # define IPV6_PREFIX_MASK 0xFF |
17 | 0 | # define IPV6_MULTICAST_PREFIX 0xFF |
18 | | #endif |
19 | | |
20 | | /* Configuration parameters */ |
21 | | |
22 | 547 | #define UDP_MANAGERPARAMS 2 |
23 | | |
24 | | static UA_KeyValueRestriction udpManagerParams[UDP_MANAGERPARAMS] = { |
25 | | {{0, UA_STRING_STATIC("recv-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false}, |
26 | | {{0, UA_STRING_STATIC("send-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false} |
27 | | }; |
28 | | |
29 | 568 | #define UDP_PARAMETERSSIZE 9 |
30 | 568 | #define UDP_PARAMINDEX_LISTEN 0 |
31 | 568 | #define UDP_PARAMINDEX_ADDR 1 |
32 | 568 | #define UDP_PARAMINDEX_PORT 2 |
33 | 568 | #define UDP_PARAMINDEX_INTERFACE 3 |
34 | 568 | #define UDP_PARAMINDEX_TTL 4 |
35 | 568 | #define UDP_PARAMINDEX_LOOPBACK 5 |
36 | 568 | #define UDP_PARAMINDEX_REUSE 6 |
37 | 568 | #define UDP_PARAMINDEX_SOCKPRIO 7 |
38 | 568 | #define UDP_PARAMINDEX_VALIDATE 8 |
39 | | |
40 | | static UA_KeyValueRestriction udpConnectionParams[UDP_PARAMETERSSIZE] = { |
41 | | {{0, UA_STRING_STATIC("listen")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false}, |
42 | | {{0, UA_STRING_STATIC("address")}, &UA_TYPES[UA_TYPES_STRING], false, true, true}, |
43 | | {{0, UA_STRING_STATIC("port")}, &UA_TYPES[UA_TYPES_UINT16], true, true, false}, |
44 | | {{0, UA_STRING_STATIC("interface")}, &UA_TYPES[UA_TYPES_STRING], false, true, false}, |
45 | | {{0, UA_STRING_STATIC("ttl")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false}, |
46 | | {{0, UA_STRING_STATIC("loopback")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false}, |
47 | | {{0, UA_STRING_STATIC("reuse")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false}, |
48 | | {{0, UA_STRING_STATIC("sockpriority")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false}, |
49 | | {{0, UA_STRING_STATIC("validate")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false} |
50 | | }; |
51 | | |
52 | | /* A registered file descriptor with an additional method pointer */ |
53 | | typedef struct { |
54 | | UA_RegisteredFD rfd; |
55 | | |
56 | | UA_ConnectionManager_connectionCallback applicationCB; |
57 | | void *application; |
58 | | void *context; |
59 | | |
60 | | struct sockaddr_storage sendAddr; |
61 | | #ifdef UA_ARCHITECTURE_WIN32 |
62 | | size_t sendAddrLength; |
63 | | #else |
64 | | socklen_t sendAddrLength; |
65 | | #endif |
66 | | } UDP_FD; |
67 | | |
68 | | typedef enum { |
69 | | MULTICASTTYPE_NONE = 0, |
70 | | MULTICASTTYPE_IPV4, |
71 | | MULTICASTTYPE_IPV6 |
72 | | } MultiCastType; |
73 | | |
74 | | typedef union { |
75 | | #if !defined(ip_mreqn) |
76 | | struct ip_mreq ipv4; |
77 | | #else |
78 | | struct ip_mreqn ipv4; |
79 | | #endif |
80 | | #if UA_IPV6 |
81 | | struct ipv6_mreq ipv6; |
82 | | #endif |
83 | | } MulticastRequest; |
84 | | |
85 | | static UA_Boolean |
86 | 568 | isMulticastAddress(const UA_Byte *address, UA_Byte mask, UA_Byte prefix) { |
87 | 568 | return (address[0] & mask) == prefix; |
88 | 568 | } |
89 | | |
90 | | static MultiCastType |
91 | 568 | multiCastType(struct addrinfo *info) { |
92 | 568 | const UA_Byte *address; |
93 | 568 | if(info->ai_family == AF_INET) { |
94 | 568 | address = (UA_Byte *)&((struct sockaddr_in *)info->ai_addr)->sin_addr; |
95 | 568 | if(isMulticastAddress(address, IPV4_PREFIX_MASK, IPV4_MULTICAST_PREFIX)) |
96 | 568 | return MULTICASTTYPE_IPV4; |
97 | 568 | #if UA_IPV6 |
98 | 568 | } else if(info->ai_family == AF_INET6) { |
99 | 0 | address = (UA_Byte *)&((struct sockaddr_in6 *)info->ai_addr)->sin6_addr; |
100 | 0 | if(isMulticastAddress(address, IPV6_PREFIX_MASK, IPV6_MULTICAST_PREFIX)) |
101 | 0 | return MULTICASTTYPE_IPV6; |
102 | 0 | #endif |
103 | 0 | } |
104 | 0 | return MULTICASTTYPE_NONE; |
105 | 568 | } |
106 | | |
107 | | #ifdef UA_ARCHITECTURE_WIN32 |
108 | | |
109 | | #define ADDR_BUFFER_SIZE 15000 /* recommended size in the MSVC docs */ |
110 | | |
111 | | static UA_StatusCode |
112 | | setMulticastInterface(const char *netif, struct addrinfo *info, |
113 | | MulticastRequest *req, const UA_Logger *logger) { |
114 | | ULONG outBufLen = ADDR_BUFFER_SIZE; |
115 | | UA_STACKARRAY(char, addrBuf, ADDR_BUFFER_SIZE); |
116 | | |
117 | | /* Get the network interface descriptions */ |
118 | | ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | |
119 | | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; |
120 | | PIP_ADAPTER_ADDRESSES ifaddr = (IP_ADAPTER_ADDRESSES *)addrBuf; |
121 | | DWORD ret = GetAdaptersAddresses(info->ai_family, flags, NULL, ifaddr, &outBufLen); |
122 | | if(ret != NO_ERROR) { |
123 | | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, |
124 | | "UDP\t| Interface configuration preparation failed"); |
125 | | return UA_STATUSCODE_BADINTERNALERROR; |
126 | | } |
127 | | |
128 | | /* Iterate through linked list of network interfaces */ |
129 | | char sourceAddr[64]; |
130 | | unsigned int idx = 0; |
131 | | for(PIP_ADAPTER_ADDRESSES ifa = ifaddr; ifa != NULL; ifa = ifa->Next) { |
132 | | idx = (info->ai_family == AF_INET) ? ifa->IfIndex : ifa->Ipv6IfIndex; |
133 | | |
134 | | /* Check if network interface name matches */ |
135 | | if(strcmp(ifa->AdapterName, netif) == 0) |
136 | | goto done; |
137 | | |
138 | | /* Check if ip address matches */ |
139 | | for(PIP_ADAPTER_UNICAST_ADDRESS u = ifa->FirstUnicastAddress; u; u = u->Next) { |
140 | | LPSOCKADDR addr = u->Address.lpSockaddr; |
141 | | if(addr->sa_family == AF_INET) { |
142 | | inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, |
143 | | sourceAddr, sizeof(sourceAddr)); |
144 | | } else if(addr->sa_family == AF_INET6) { |
145 | | inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, |
146 | | sourceAddr, sizeof(sourceAddr)); |
147 | | } else { |
148 | | continue; |
149 | | } |
150 | | if(strcmp(sourceAddr, netif) == 0) |
151 | | goto done; |
152 | | } |
153 | | } |
154 | | |
155 | | /* Not matching interface found */ |
156 | | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, |
157 | | "UDP\t| Interface configuration preparation failed " |
158 | | "(interface %s not found)", netif); |
159 | | return UA_STATUSCODE_BADINTERNALERROR; |
160 | | |
161 | | done: |
162 | | /* Write the interface index */ |
163 | | if(info->ai_family == AF_INET) |
164 | | /* MSVC documentation of struct ip_mreq: To use an interface index of 1 |
165 | | * would be the same as an IP address of 0.0.0.1. */ |
166 | | req->ipv4.imr_interface.s_addr = htonl(idx); |
167 | | #if UA_IPV6 |
168 | | else /* if(info->ai_family == AF_INET6) */ |
169 | | req->ipv6.ipv6mr_interface = idx; |
170 | | #endif |
171 | | return UA_STATUSCODE_GOOD; |
172 | | } |
173 | | |
174 | | #else |
175 | | |
176 | | static UA_StatusCode |
177 | | setMulticastInterface(const char *netif, struct addrinfo *info, |
178 | 0 | MulticastRequest *req, const UA_Logger *logger) { |
179 | 0 | UA_RESET_ERRNO; |
180 | 0 | struct ifaddrs *ifaddr; |
181 | 0 | int ret = getifaddrs(&ifaddr); |
182 | 0 | if(ret == -1) { |
183 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
184 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, |
185 | 0 | "UDP\t| Interface configuration preparation failed " |
186 | 0 | "(getifaddrs error: %s)", errno_str)); |
187 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
188 | 0 | } |
189 | | |
190 | | /* Iterate over the interfaces */ |
191 | 0 | unsigned int idx = 0; |
192 | 0 | struct ifaddrs *ifa = NULL; |
193 | 0 | for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { |
194 | 0 | if(!ifa->ifa_addr) |
195 | 0 | continue; |
196 | | |
197 | | /* Does the protocol family match? */ |
198 | 0 | if(ifa->ifa_addr->sa_family != info->ai_family) |
199 | 0 | continue; |
200 | | |
201 | | #if defined(UA_ARCHITECTURE_WIN32) || defined(ip_mreqn) |
202 | | idx = UA_if_nametoindex(ifa->ifa_name); |
203 | | if(idx == 0) |
204 | | continue; |
205 | | #endif |
206 | | |
207 | | /* Found network interface by name */ |
208 | 0 | if(strcmp(ifa->ifa_name, netif) == 0) |
209 | 0 | break; |
210 | | |
211 | | /* Check if the interface name is an IP address that matches */ |
212 | 0 | UA_RESET_ERRNO; |
213 | 0 | char host[NI_MAXHOST]; |
214 | 0 | ret = getnameinfo(ifa->ifa_addr, |
215 | 0 | (info->ai_family == AF_INET) ? |
216 | 0 | sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), |
217 | 0 | host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); |
218 | 0 | if(ret != 0) { |
219 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
220 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, |
221 | 0 | "UDP\t| Interface configuration preparation " |
222 | 0 | "failed (Error: %s).", errno_str)); |
223 | 0 | freeifaddrs(ifaddr); |
224 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
225 | 0 | } |
226 | 0 | if(strcmp(host, netif) == 0) |
227 | 0 | break; |
228 | 0 | } |
229 | | |
230 | 0 | freeifaddrs(ifaddr); |
231 | 0 | if(!ifa) |
232 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
233 | | |
234 | | /* Write the interface index */ |
235 | 0 | if(info->ai_family == AF_INET) { |
236 | | #if defined(ip_mreqn) |
237 | | req->ipv4.imr_ifindex = idx; |
238 | | #endif |
239 | 0 | #if UA_IPV6 |
240 | 0 | } else { /* if(info->ai_family == AF_INET6) */ |
241 | 0 | req->ipv6.ipv6mr_interface = idx; |
242 | 0 | #endif |
243 | 0 | } |
244 | 0 | return UA_STATUSCODE_GOOD; |
245 | 0 | } |
246 | | |
247 | | #endif /* UA_ARCHITECTURE_WIN32 */ |
248 | | |
249 | | static UA_StatusCode |
250 | | setupMulticastRequest(UA_FD socket, MulticastRequest *req, const UA_KeyValueMap *params, |
251 | 568 | struct addrinfo *info, const UA_Logger *logger) { |
252 | | /* Initialize the address information */ |
253 | 568 | if(info->ai_family == AF_INET) { |
254 | 568 | struct sockaddr_in *sin = (struct sockaddr_in *)info->ai_addr; |
255 | 568 | req->ipv4.imr_multiaddr = sin->sin_addr; |
256 | 568 | #if !defined(ip_mreqn) |
257 | 568 | req->ipv4.imr_interface.s_addr = htonl(INADDR_ANY); /* default ANY */ |
258 | | #else |
259 | | req->ipv4.imr_address.s_addr = htonl(INADDR_ANY); /* default ANY */ |
260 | | req->ipv4.imr_ifindex = 0; |
261 | | #endif |
262 | 568 | #if UA_IPV6 |
263 | 568 | } else if(info->ai_family == AF_INET6) { |
264 | 0 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)info->ai_addr; |
265 | 0 | req->ipv6.ipv6mr_multiaddr = sin6->sin6_addr; |
266 | 0 | req->ipv6.ipv6mr_interface = 0; /* default ANY interface */ |
267 | 0 | #endif |
268 | 0 | } else { |
269 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, |
270 | 0 | "UDP\t| Multicast configuration failed: Unknown protocol family"); |
271 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
272 | 0 | } |
273 | | |
274 | | /* Was an interface (or local IP address) defined? */ |
275 | 568 | const UA_String *netif = (const UA_String*) |
276 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_INTERFACE].name, |
277 | 568 | &UA_TYPES[UA_TYPES_STRING]); |
278 | 568 | if(!netif) { |
279 | 568 | UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK, |
280 | 568 | "UDP %u\t| No network interface defined for multicast. " |
281 | 568 | "The first suitable network interface is used.", |
282 | 568 | (unsigned)socket); |
283 | 568 | return UA_STATUSCODE_GOOD; |
284 | 568 | } |
285 | | |
286 | | /* Set the interface index */ |
287 | 0 | UA_STACKARRAY(char, interfaceAsChar, sizeof(char) * netif->length + 1); |
288 | 0 | memcpy(interfaceAsChar, netif->data, netif->length); |
289 | 0 | interfaceAsChar[netif->length] = 0; |
290 | 0 | return setMulticastInterface(interfaceAsChar, info, req, logger); |
291 | 568 | } |
292 | | |
293 | | /* Retrieves hostname and port from given key value parameters. |
294 | | * |
295 | | * @param[in] params the parameter map to retrieve from |
296 | | * @param[out] hostname the retrieved hostname when present, NULL otherwise |
297 | | * @param[out] portStr the retrieved port when present, NULL otherwise |
298 | | * @param[in] logger the logger to log information |
299 | | * @return -1 upon error, 0 if there was no host or port parameter, 1 if |
300 | | * host and port are present */ |
301 | | static int |
302 | | getHostAndPortFromParams(const UA_KeyValueMap *params, char *hostname, |
303 | 284 | char *portStr, const UA_Logger *logger) { |
304 | | /* Prepare the port parameter as a string */ |
305 | 284 | const UA_UInt16 *port = (const UA_UInt16*) |
306 | 284 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_PORT].name, |
307 | 284 | &UA_TYPES[UA_TYPES_UINT16]); |
308 | 284 | UA_assert(port); /* checked before */ |
309 | 284 | mp_snprintf(portStr, UA_MAXPORTSTR_LENGTH, "%d", *port); |
310 | | |
311 | | /* Prepare the hostname string */ |
312 | 284 | const UA_String *host = (const UA_String*) |
313 | 284 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_ADDR].name, |
314 | 284 | &UA_TYPES[UA_TYPES_STRING]); |
315 | 284 | if(!host) { |
316 | 0 | UA_LOG_DEBUG(logger, UA_LOGCATEGORY_NETWORK, |
317 | 0 | "UDP\t| No address configured"); |
318 | 0 | return -1; |
319 | 0 | } |
320 | 284 | if(host->length >= UA_MAXHOSTNAME_LENGTH) { |
321 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_EVENTLOOP, |
322 | 0 | "UDP\t| Open UDP Connection: Hostname too long, aborting"); |
323 | 0 | return -1; |
324 | 0 | } |
325 | 284 | strncpy(hostname, (const char*)host->data, host->length); |
326 | 284 | hostname[host->length] = 0; |
327 | 284 | return 1; |
328 | 284 | } |
329 | | |
330 | | static int |
331 | | getConnectionInfoFromParams(const UA_KeyValueMap *params, |
332 | | char *hostname, char *portStr, |
333 | 284 | struct addrinfo **info, const UA_Logger *logger) { |
334 | 284 | int foundParams = getHostAndPortFromParams(params, hostname, portStr, logger); |
335 | 284 | if(foundParams < 0) |
336 | 0 | return -1; |
337 | | |
338 | | /* Create the socket description from the connectString |
339 | | * TODO: Make this non-blocking */ |
340 | 284 | UA_RESET_ERRNO; |
341 | 284 | struct addrinfo hints; |
342 | 284 | memset(&hints, 0, sizeof(struct addrinfo)); |
343 | 284 | hints.ai_family = AF_UNSPEC; |
344 | 284 | hints.ai_socktype = SOCK_DGRAM; |
345 | 284 | int error = UA_getaddrinfo(hostname, portStr, &hints, info); |
346 | 284 | if(error != 0) { |
347 | | #ifdef UA_ARCHITECTURE_WIN32 |
348 | | UA_LOG_SOCKET_ERRNO_GAI_WRAP( |
349 | | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
350 | | "UDP\t| Lookup of %s failed with error %d - %s", |
351 | | hostname, error, errno_str)); |
352 | | #else |
353 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
354 | 0 | "UDP\t| Lookup of %s failed with error %s", |
355 | 0 | hostname, gai_strerror(error)); |
356 | 0 | #endif |
357 | 0 | return -1; |
358 | 0 | } |
359 | 284 | return 1; |
360 | 284 | } |
361 | | |
362 | | /* Set loop back data to your host */ |
363 | | static UA_StatusCode |
364 | | setLoopBackData(UA_SOCKET sockfd, UA_Boolean enableLoopback, |
365 | 0 | int ai_family, const UA_Logger *logger) { |
366 | 0 | UA_RESET_ERRNO; |
367 | | /* The loopback option has a different integer size between IPv4 and IPv6. |
368 | | * Some operating systems (e.g. OpenBSD) handle this very strict. Hence the |
369 | | * different "enable" variables below are required. */ |
370 | 0 | int retcode; |
371 | 0 | #if UA_IPV6 |
372 | 0 | if(ai_family == AF_INET6) { |
373 | 0 | unsigned int enable6 = enableLoopback; |
374 | 0 | retcode = UA_setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, |
375 | 0 | &enable6, sizeof(enable6)); |
376 | 0 | } else |
377 | 0 | #endif |
378 | 0 | { |
379 | 0 | unsigned char enable = enableLoopback; |
380 | 0 | retcode = UA_setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, |
381 | 0 | &enable, sizeof (enable)); |
382 | 0 | } |
383 | 0 | if(retcode < 0) { |
384 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
385 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_NETWORK, |
386 | 0 | "UDP %u\t| Loopback setup failed: " |
387 | 0 | "Cannot set socket option IP_MULTICAST_LOOP. Error: %s", |
388 | 0 | (unsigned)sockfd, errno_str)); |
389 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
390 | 0 | } |
391 | 0 | return UA_STATUSCODE_GOOD; |
392 | 0 | } |
393 | | |
394 | | static UA_StatusCode |
395 | | setTimeToLive(UA_SOCKET sockfd, UA_UInt32 messageTTL, |
396 | 568 | int ai_family, const UA_Logger *logger) { |
397 | 568 | UA_RESET_ERRNO; |
398 | | /* Set Time to live (TTL). Value of 1 prevent forward beyond the local network. */ |
399 | 568 | #if UA_IPV6 |
400 | 568 | if(UA_setsockopt(sockfd, |
401 | 568 | ai_family == PF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP, |
402 | 568 | ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL, |
403 | 568 | (const char *)&messageTTL, |
404 | 568 | sizeof(messageTTL)) < 0) |
405 | | #else |
406 | | if(UA_setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, |
407 | | (const char *)&messageTTL, |
408 | | sizeof(messageTTL)) < 0) |
409 | | #endif |
410 | 0 | { |
411 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
412 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
413 | 0 | "UDP %u\t| Time to live setup failed: " |
414 | 0 | "Cannot set socket option IP_MULTICAST_TTL. Error: %s", |
415 | 0 | (unsigned)sockfd, errno_str)); |
416 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
417 | 0 | } |
418 | 568 | return UA_STATUSCODE_GOOD; |
419 | 568 | } |
420 | | |
421 | | static UA_StatusCode |
422 | 568 | setReuseAddress(UA_SOCKET sockfd, UA_Boolean enableReuse, const UA_Logger *logger) { |
423 | | /* Set reuse address -> enables sharing of the same listening address on |
424 | | * different sockets */ |
425 | 568 | UA_RESET_ERRNO; |
426 | 568 | int enableReuseVal = (enableReuse) ? 1 : 0; |
427 | 568 | if(UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, |
428 | 568 | (const char*)&enableReuseVal, sizeof(enableReuseVal)) < 0) { |
429 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
430 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
431 | 0 | "UDP %u\t| Reuse address setup failed: " |
432 | 0 | "Cannot set socket option SO_REUSEADDR. Error: %s", |
433 | 0 | (unsigned)sockfd, errno_str)); |
434 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
435 | 0 | } |
436 | 568 | return UA_STATUSCODE_GOOD; |
437 | 568 | } |
438 | | |
439 | | #ifdef __linux__ |
440 | | static UA_StatusCode |
441 | | setSocketPriority(UA_SOCKET sockfd, UA_UInt32 socketPriority, |
442 | 0 | const UA_Logger *logger) { |
443 | 0 | UA_RESET_ERRNO; |
444 | 0 | int prio = (int)socketPriority; |
445 | 0 | if(UA_setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(int)) < 0) { |
446 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
447 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_NETWORK, |
448 | 0 | "UDP %u\t| Socket priority setup failed: " |
449 | 0 | "Cannot set socket option SO_PRIORITY. Error: %s", |
450 | 0 | (unsigned)sockfd, errno_str)); |
451 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
452 | 0 | } |
453 | 0 | return UA_STATUSCODE_GOOD; |
454 | 0 | } |
455 | | #endif |
456 | | |
457 | | static UA_StatusCode |
458 | | setConnectionConfig(UA_FD socket, const UA_KeyValueMap *params, |
459 | 568 | int ai_family, const UA_Logger *logger) { |
460 | | /* Set socket config that is always set */ |
461 | 568 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
462 | 568 | res |= UA_EventLoopPOSIX_setNonBlocking(socket); |
463 | 568 | res |= UA_EventLoopPOSIX_setNoSigPipe(socket); |
464 | 568 | if(res != UA_STATUSCODE_GOOD) |
465 | 0 | return res; |
466 | | |
467 | | /* Some Linux distributions have net.ipv6.bindv6only not activated. So |
468 | | * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use |
469 | | * AF_INET6 sockets only for IPv6. */ |
470 | 568 | #if UA_IPV6 |
471 | 568 | int optval = 1; |
472 | 568 | if(ai_family == AF_INET6 && |
473 | 0 | UA_setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, |
474 | 0 | (const char*)&optval, sizeof(optval)) == -1) { |
475 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
476 | 0 | "UDP %u\t| Could not set an IPv6 socket to IPv6 only, closing", |
477 | 0 | (unsigned)socket); |
478 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
479 | 0 | } |
480 | 568 | #endif |
481 | | |
482 | 568 | UA_RESET_ERRNO; |
483 | | |
484 | | /* Set socket settings from the parameters */ |
485 | 568 | const UA_UInt32 *messageTTL = (const UA_UInt32*) |
486 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_TTL].name, |
487 | 568 | &UA_TYPES[UA_TYPES_UINT32]); |
488 | 568 | if(messageTTL) |
489 | 568 | res |= setTimeToLive(socket, *messageTTL, ai_family, logger); |
490 | | |
491 | 568 | const UA_Boolean *enableLoopback = (const UA_Boolean*) |
492 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_LOOPBACK].name, |
493 | 568 | &UA_TYPES[UA_TYPES_BOOLEAN]); |
494 | 568 | if(enableLoopback) |
495 | 0 | res |= setLoopBackData(socket, *enableLoopback, ai_family, logger); |
496 | | |
497 | 568 | const UA_Boolean *enableReuse = (const UA_Boolean*) |
498 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_REUSE].name, |
499 | 568 | &UA_TYPES[UA_TYPES_BOOLEAN]); |
500 | 568 | if(enableReuse) |
501 | 568 | res |= setReuseAddress(socket, *enableReuse, logger); |
502 | | |
503 | 568 | #ifdef __linux__ |
504 | 568 | const UA_UInt32 *socketPriority = (const UA_UInt32*) |
505 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_SOCKPRIO].name, |
506 | 568 | &UA_TYPES[UA_TYPES_UINT32]); |
507 | 568 | if(socketPriority) |
508 | 0 | res |= setSocketPriority(socket, *socketPriority, logger); |
509 | 568 | #endif |
510 | | |
511 | 568 | if(res != UA_STATUSCODE_GOOD) { |
512 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
513 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
514 | 0 | "UDP\t| Could not set socket options: %s", errno_str)); |
515 | 0 | } |
516 | 568 | return res; |
517 | 568 | } |
518 | | |
519 | | static UA_StatusCode |
520 | | setupListenMultiCast(UA_FD fd, struct addrinfo *info, const UA_KeyValueMap *params, |
521 | 284 | MultiCastType multiCastType, const UA_Logger *logger) { |
522 | 284 | MulticastRequest req; |
523 | 284 | UA_StatusCode res = setupMulticastRequest(fd, &req, params, info, logger); |
524 | 284 | if(res != UA_STATUSCODE_GOOD) |
525 | 0 | return res; |
526 | | |
527 | 284 | UA_RESET_ERRNO; |
528 | 284 | int result = -1; |
529 | 284 | if(info->ai_family == AF_INET && multiCastType == MULTICASTTYPE_IPV4) { |
530 | 284 | result = UA_setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
531 | 284 | &req.ipv4, sizeof(req.ipv4)); |
532 | 284 | #if UA_IPV6 |
533 | 284 | } else if(info->ai_family == AF_INET6 && multiCastType == MULTICASTTYPE_IPV6) { |
534 | 0 | result = UA_setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, |
535 | 0 | &req.ipv6, sizeof(req.ipv6)); |
536 | 0 | #endif |
537 | 0 | } |
538 | | |
539 | 284 | if(result < 0) { |
540 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
541 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_NETWORK, |
542 | 0 | "UDP %u\t| Cannot set socket for multicast receiving. Error: %s", |
543 | 0 | (unsigned)fd, errno_str)); |
544 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
545 | 0 | } |
546 | 284 | return UA_STATUSCODE_GOOD; |
547 | 284 | } |
548 | | |
549 | | static UA_StatusCode |
550 | | setupSendMultiCast(UA_FD fd, struct addrinfo *info, const UA_KeyValueMap *params, |
551 | 284 | MultiCastType multiCastType, const UA_Logger *logger) { |
552 | 284 | MulticastRequest req; |
553 | 284 | UA_StatusCode res = setupMulticastRequest(fd, &req, params, info, logger); |
554 | 284 | if(res != UA_STATUSCODE_GOOD) |
555 | 0 | return res; |
556 | | |
557 | 284 | UA_RESET_ERRNO; |
558 | 284 | int result = -1; |
559 | 284 | if(info->ai_family == AF_INET && multiCastType == MULTICASTTYPE_IPV4) { |
560 | | #ifdef UA_ARCHITECTURE_WIN32 |
561 | | result = UA_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, |
562 | | (const char *)&req.ipv4.imr_interface, |
563 | | sizeof(struct in_addr)); |
564 | | #else |
565 | 284 | result = UA_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, |
566 | 284 | &req.ipv4, sizeof(req.ipv4)); |
567 | 284 | #endif |
568 | 284 | #if UA_IPV6 |
569 | 284 | } else if(info->ai_family == AF_INET6 && multiCastType == MULTICASTTYPE_IPV6) { |
570 | 0 | result = UA_setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, |
571 | 0 | (const char *) |
572 | 0 | &req.ipv6.ipv6mr_interface, sizeof(req.ipv6.ipv6mr_interface)); |
573 | 0 | #endif |
574 | 0 | } |
575 | | |
576 | 284 | if(result < 0) { |
577 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
578 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_NETWORK, |
579 | 0 | "UDP %u\t| Cannot set socket for multicast sending. Error: %s", |
580 | 0 | (unsigned)fd, errno_str)); |
581 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
582 | 0 | } |
583 | 284 | return UA_STATUSCODE_GOOD; |
584 | 284 | } |
585 | | |
586 | | /* Test if the ConnectionManager can be stopped */ |
587 | | static void |
588 | 1.11k | UDP_checkStopped(UA_POSIXConnectionManager *pcm) { |
589 | 1.11k | UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex); |
590 | | |
591 | 1.11k | if(pcm->fdsSize == 0 && |
592 | 831 | pcm->cm.eventSource.state == UA_EVENTSOURCESTATE_STOPPING) { |
593 | 547 | UA_LOG_DEBUG(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK, |
594 | 547 | "UDP\t| All sockets closed, the EventLoop has stopped"); |
595 | 547 | pcm->cm.eventSource.state = UA_EVENTSOURCESTATE_STOPPED; |
596 | 547 | } |
597 | 1.11k | } |
598 | | |
599 | | /* This method must not be called from the application directly, but from within |
600 | | * the EventLoop. Otherwise we cannot be sure whether the file descriptor is |
601 | | * still used after calling close. */ |
602 | | static void |
603 | 568 | UDP_close(UA_POSIXConnectionManager *pcm, UDP_FD *conn) { |
604 | 568 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop; |
605 | 568 | UA_LOCK_ASSERT(&el->elMutex); |
606 | | |
607 | 568 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
608 | 568 | "UDP %u\t| Closing connection", |
609 | 568 | (unsigned)conn->rfd.fd); |
610 | | |
611 | | /* Deregister from the EventLoop */ |
612 | 568 | UA_EventLoopPOSIX_deregisterFD(el, &conn->rfd); |
613 | | |
614 | | /* Deregister internally */ |
615 | 568 | ZIP_REMOVE(UA_FDTree, &pcm->fds, &conn->rfd); |
616 | 568 | UA_assert(pcm->fdsSize > 0); |
617 | 568 | pcm->fdsSize--; |
618 | | |
619 | | /* Signal closing to the application */ |
620 | 568 | conn->applicationCB(&pcm->cm, (uintptr_t)conn->rfd.fd, |
621 | 568 | conn->application, &conn->context, |
622 | 568 | UA_CONNECTIONSTATE_CLOSING, |
623 | 568 | &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL); |
624 | | |
625 | | /* Close the socket */ |
626 | 568 | UA_RESET_ERRNO; |
627 | 568 | int ret = UA_close(conn->rfd.fd); |
628 | 568 | if(ret == 0) { |
629 | 568 | UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
630 | 568 | "UDP %u\t| Socket closed", (unsigned)conn->rfd.fd); |
631 | 568 | } else { |
632 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
633 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
634 | 0 | "UDP %u\t| Could not close the socket (%s)", |
635 | 0 | (unsigned)conn->rfd.fd, errno_str)); |
636 | 0 | } |
637 | | |
638 | 568 | UA_free(conn); |
639 | | |
640 | | /* Stop if the ucm is stopping and this was the last open socket */ |
641 | 568 | UDP_checkStopped(pcm); |
642 | 568 | } |
643 | | |
644 | | static void |
645 | 568 | UDP_delayedClose(void *application, void *context) { |
646 | 568 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application; |
647 | 568 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop; |
648 | 568 | UDP_FD *conn = (UDP_FD*)context; |
649 | 568 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
650 | 568 | "UDP %u\t| Delayed closing of the connection", |
651 | 568 | (unsigned)conn->rfd.fd); |
652 | 568 | UA_LOCK(&el->elMutex); |
653 | 568 | UDP_close(pcm, conn); |
654 | 568 | UA_UNLOCK(&el->elMutex); |
655 | 568 | } |
656 | | |
657 | | /* Gets called when a socket receives data or closes */ |
658 | | static void |
659 | | UDP_connectionSocketCallback(UA_POSIXConnectionManager *pcm, UDP_FD *conn, |
660 | 0 | short event) { |
661 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop; |
662 | 0 | UA_LOCK_ASSERT(&el->elMutex); |
663 | |
|
664 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
665 | 0 | "UDP %u\t| Activity on the socket", |
666 | 0 | (unsigned)conn->rfd.fd); |
667 | |
|
668 | 0 | if(event == UA_FDEVENT_ERR) { |
669 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
670 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
671 | 0 | "UDP %u\t| recv signaled the socket was shutdown (%s)", |
672 | 0 | (unsigned)conn->rfd.fd, errno_str)); |
673 | 0 | UDP_close(pcm, conn); |
674 | 0 | return; |
675 | 0 | } |
676 | | |
677 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
678 | 0 | "UDP %u\t| Allocate receive buffer", (unsigned)conn->rfd.fd); |
679 | | |
680 | | /* Use the already allocated receive-buffer */ |
681 | 0 | UA_ByteString response = pcm->rxBuffer; |
682 | | |
683 | | /* Receive */ |
684 | 0 | UA_RESET_ERRNO; |
685 | 0 | struct sockaddr_storage source; |
686 | 0 | #ifndef UA_ARCHITECTURE_WIN32 |
687 | 0 | socklen_t sourceSize = (socklen_t)sizeof(struct sockaddr_storage); |
688 | 0 | ssize_t ret = UA_recvfrom(conn->rfd.fd, (char*)response.data, response.length, |
689 | 0 | MSG_DONTWAIT, (struct sockaddr*)&source, &sourceSize); |
690 | | #else |
691 | | int sourceSize = (int)sizeof(struct sockaddr_storage); |
692 | | int ret = UA_recvfrom(conn->rfd.fd, (char*)response.data, (int)response.length, |
693 | | MSG_DONTWAIT, (struct sockaddr*)&source, &sourceSize); |
694 | | #endif |
695 | | |
696 | | /* Receive has failed */ |
697 | 0 | if(ret <= 0) { |
698 | 0 | if(UA_ERRNO == UA_INTERRUPTED) |
699 | 0 | return; |
700 | | |
701 | | /* Orderly shutdown of the socket. We can immediately close as no method |
702 | | * "below" in the call stack will use the socket in this iteration of |
703 | | * the EventLoop. */ |
704 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
705 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
706 | 0 | "UDP %u\t| recv signaled the socket was shutdown (%s)", |
707 | 0 | (unsigned)conn->rfd.fd, errno_str)); |
708 | 0 | UDP_close(pcm, conn); |
709 | 0 | return; |
710 | 0 | } |
711 | | |
712 | 0 | response.length = (size_t)ret; /* Set the length of the received buffer */ |
713 | | |
714 | | /* Extract message source and port */ |
715 | 0 | char sourceAddr[64]; |
716 | 0 | UA_UInt16 sourcePort; |
717 | 0 | switch(source.ss_family) { |
718 | 0 | case AF_INET: |
719 | 0 | UA_inet_ntop(AF_INET, &((struct sockaddr_in *)&source)->sin_addr, |
720 | 0 | sourceAddr, 64); |
721 | 0 | sourcePort = htons(((struct sockaddr_in *)&source)->sin_port); |
722 | 0 | break; |
723 | 0 | case AF_INET6: |
724 | 0 | UA_inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)&source)->sin6_addr), |
725 | 0 | sourceAddr, 64); |
726 | 0 | sourcePort = htons(((struct sockaddr_in6 *)&source)->sin6_port); |
727 | 0 | break; |
728 | 0 | default: |
729 | 0 | sourceAddr[0] = 0; |
730 | 0 | sourcePort = 0; |
731 | 0 | } |
732 | | |
733 | 0 | UA_String sourceAddrStr = UA_STRING(sourceAddr); |
734 | 0 | UA_KeyValuePair kvp[2]; |
735 | 0 | kvp[0].key = UA_QUALIFIEDNAME(0, "remote-address"); |
736 | 0 | UA_Variant_setScalar(&kvp[0].value, &sourceAddrStr, &UA_TYPES[UA_TYPES_STRING]); |
737 | 0 | kvp[1].key = UA_QUALIFIEDNAME(0, "remote-port"); |
738 | 0 | UA_Variant_setScalar(&kvp[1].value, &sourcePort, &UA_TYPES[UA_TYPES_UINT16]); |
739 | 0 | UA_KeyValueMap kvm = {2, kvp}; |
740 | |
|
741 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
742 | 0 | "UDP %u\t| Received message of size %u from %s on port %u", |
743 | 0 | (unsigned)conn->rfd.fd, (unsigned)ret, |
744 | 0 | sourceAddr, sourcePort); |
745 | | |
746 | | /* Callback to the application layer */ |
747 | 0 | conn->applicationCB(&pcm->cm, (uintptr_t)conn->rfd.fd, |
748 | 0 | conn->application, &conn->context, |
749 | 0 | UA_CONNECTIONSTATE_ESTABLISHED, |
750 | 0 | &kvm, response); |
751 | 0 | } |
752 | | |
753 | | static UA_StatusCode |
754 | | UDP_registerListenSocket(UA_POSIXConnectionManager *pcm, UA_UInt16 port, |
755 | | struct addrinfo *info, const UA_KeyValueMap *params, |
756 | | void *application, void *context, |
757 | | UA_ConnectionManager_connectionCallback connectionCallback, |
758 | 284 | UA_Boolean validate) { |
759 | 284 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop; |
760 | 284 | UA_LOCK_ASSERT(&el->elMutex); |
761 | | |
762 | | /* Get logging information */ |
763 | 284 | UA_RESET_ERRNO; |
764 | 284 | char hoststr[UA_MAXHOSTNAME_LENGTH]; |
765 | 284 | int get_res = UA_getnameinfo(info->ai_addr, info->ai_addrlen, |
766 | 284 | hoststr, sizeof(hoststr), |
767 | 284 | NULL, 0, NI_NUMERICHOST); |
768 | 284 | if(get_res != 0) { |
769 | 0 | hoststr[0] = 0; |
770 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
771 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
772 | 0 | "UDP\t| Could not resolve the hostname (Error: %s)", errno_str)); |
773 | 0 | if(validate) |
774 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
775 | 0 | } |
776 | | |
777 | | /* Create the listen socket */ |
778 | 284 | UA_RESET_ERRNO; |
779 | 284 | UA_FD listenSocket = UA_socket(info->ai_family, info->ai_socktype, info->ai_protocol); |
780 | 284 | if(listenSocket == UA_INVALID_FD) { |
781 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
782 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
783 | 0 | "UDP\t| Error opening the listen socket for " |
784 | 0 | "\"%s\" on port %u (Error: %s)", |
785 | 0 | (unsigned)listenSocket, hoststr, port, errno_str)); |
786 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
787 | 0 | } |
788 | | |
789 | | /* Set the socket configuration per the parameters */ |
790 | 284 | UA_StatusCode res = |
791 | 284 | setConnectionConfig(listenSocket, params, |
792 | 284 | info->ai_family, el->eventLoop.logger); |
793 | 284 | if(res != UA_STATUSCODE_GOOD) { |
794 | 0 | UA_close(listenSocket); |
795 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
796 | 0 | } |
797 | | |
798 | | /* Are we going to prepare a socket for multicast? */ |
799 | 284 | MultiCastType mc = multiCastType(info); |
800 | | |
801 | | /* Bind socket to the address */ |
802 | 284 | UA_RESET_ERRNO; |
803 | | #ifdef UA_ARCHITECTURE_WIN32 |
804 | | /* On windows we need to bind the socket to INADDR_ANY before registering |
805 | | * for the multicast group */ |
806 | | int ret = -1; |
807 | | if(mc != MULTICASTTYPE_NONE) { |
808 | | if(info->ai_family == AF_INET) { |
809 | | struct sockaddr_in *orig = (struct sockaddr_in *)info->ai_addr; |
810 | | struct sockaddr_in sin; |
811 | | memset(&sin, 0, sizeof(sin)); |
812 | | sin.sin_family = AF_INET; |
813 | | sin.sin_addr.s_addr = htonl(INADDR_ANY); |
814 | | sin.sin_port = orig->sin_port; |
815 | | ret = bind(listenSocket, (struct sockaddr*)&sin, sizeof(sin)); |
816 | | } else if(info->ai_family == AF_INET6) { |
817 | | struct sockaddr_in6 *orig = (struct sockaddr_in6 *)info->ai_addr; |
818 | | struct sockaddr_in6 sin6; |
819 | | memset(&sin6, 0, sizeof(sin6)); |
820 | | sin6.sin6_family = AF_INET6; |
821 | | sin6.sin6_addr = in6addr_any; |
822 | | sin6.sin6_port = orig->sin6_port; |
823 | | ret = bind(listenSocket, (struct sockaddr*)&sin6, sizeof(sin6)); |
824 | | } |
825 | | } else { |
826 | | ret = UA_bind(listenSocket, info->ai_addr, (socklen_t)info->ai_addrlen); |
827 | | } |
828 | | #else |
829 | 284 | int ret = UA_bind(listenSocket, info->ai_addr, (socklen_t)info->ai_addrlen); |
830 | 284 | #endif |
831 | | |
832 | | /* Get the port being used if dynamic porting was used */ |
833 | 284 | if(port == 0) { |
834 | 0 | struct sockaddr_in sin; |
835 | 0 | memset(&sin, 0, sizeof(sin)); |
836 | 0 | socklen_t len = sizeof(sin); |
837 | 0 | if(getsockname(listenSocket, (struct sockaddr *)&sin, &len) != 0) { |
838 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
839 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
840 | 0 | "UDP %u\t| getsockname failed (%s)", |
841 | 0 | (unsigned)listenSocket, errno_str)); |
842 | 0 | UA_close(listenSocket); |
843 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
844 | 0 | } |
845 | 0 | port = ntohs(sin.sin_port); |
846 | 0 | } |
847 | | |
848 | 284 | if(ret < 0) { |
849 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
850 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
851 | 0 | "UDP\t| Failed to bind listen socket for \"%s\" on port %u " |
852 | 0 | "(Error: %s)", hoststr, port, errno_str)); |
853 | 0 | UA_close(listenSocket); |
854 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
855 | 0 | } |
856 | | |
857 | | /* Enable multicast if this is a multicast address */ |
858 | 284 | if(mc != MULTICASTTYPE_NONE) { |
859 | 284 | UA_RESET_ERRNO; |
860 | 284 | res = setupListenMultiCast(listenSocket, info, params, mc, |
861 | 284 | (validate) ? NULL : el->eventLoop.logger); |
862 | 284 | if(res != UA_STATUSCODE_GOOD) { |
863 | 0 | if(!validate) { |
864 | 0 | UA_LOG_SOCKET_ERRNO_GAI_WRAP( |
865 | 0 | UA_LOG_WARNING(pcm->cm.eventSource.eventLoop->logger, |
866 | 0 | UA_LOGCATEGORY_NETWORK, |
867 | 0 | "UDP\t| Failed to set up multicast for \"%s\" on port %u (%s)", |
868 | 0 | hoststr, port, errno_str)); |
869 | 0 | } else { |
870 | 0 | UA_LOG_SOCKET_ERRNO_GAI_WRAP( |
871 | 0 | UA_LOG_WARNING(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK, |
872 | 0 | "UDP\t| Failed to validate multicast for \"%s\" on port %u (%s)", |
873 | 0 | hoststr, port, errno_str)); |
874 | 0 | } |
875 | 0 | UA_close(listenSocket); |
876 | 0 | return res; |
877 | 0 | } |
878 | 284 | } |
879 | | |
880 | | /* Validation is complete - close and return */ |
881 | 284 | if(validate) { |
882 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
883 | 0 | "UDP\t| Listen socket for \"%s\" on port %u validated", hoststr, port); |
884 | 0 | UA_close(listenSocket); |
885 | 0 | return UA_STATUSCODE_GOOD; |
886 | 0 | } |
887 | | |
888 | 284 | UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
889 | 284 | "UDP %u\t| New listen socket for \"%s\" on port %u", |
890 | 284 | (unsigned)listenSocket, hoststr, port); |
891 | | |
892 | | /* Allocate the UA_RegisteredFD */ |
893 | 284 | UDP_FD *newudpfd = (UDP_FD*)UA_calloc(1, sizeof(UDP_FD)); |
894 | 284 | if(!newudpfd) { |
895 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
896 | 0 | "UDP %u\t| Error allocating memory for the socket, closing", |
897 | 0 | (unsigned)listenSocket); |
898 | 0 | UA_close(listenSocket); |
899 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
900 | 0 | } |
901 | | |
902 | 284 | newudpfd->rfd.fd = listenSocket; |
903 | 284 | newudpfd->rfd.es = &pcm->cm.eventSource; |
904 | 284 | newudpfd->rfd.listenEvents = UA_FDEVENT_IN; |
905 | 284 | newudpfd->rfd.eventSourceCB = (UA_FDCallback)UDP_connectionSocketCallback; |
906 | 284 | newudpfd->applicationCB = connectionCallback; |
907 | 284 | newudpfd->application = application; |
908 | 284 | newudpfd->context = context; |
909 | | |
910 | | /* Register in the EventLoop */ |
911 | 284 | res = UA_EventLoopPOSIX_registerFD(el, &newudpfd->rfd); |
912 | 284 | if(res != UA_STATUSCODE_GOOD) { |
913 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
914 | 0 | "UDP %u\t| Error registering the socket, closing", |
915 | 0 | (unsigned)listenSocket); |
916 | 0 | UA_free(newudpfd); |
917 | 0 | UA_close(listenSocket); |
918 | 0 | return res; |
919 | 0 | } |
920 | | |
921 | | /* Register internally in the EventSource */ |
922 | 284 | ZIP_INSERT(UA_FDTree, &pcm->fds, &newudpfd->rfd); |
923 | 284 | pcm->fdsSize++; |
924 | | |
925 | | /* Register the listen socket in the application */ |
926 | 284 | connectionCallback(&pcm->cm, (uintptr_t)newudpfd->rfd.fd, |
927 | 284 | application, &newudpfd->context, |
928 | 284 | UA_CONNECTIONSTATE_ESTABLISHED, |
929 | 284 | &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL); |
930 | 284 | return UA_STATUSCODE_GOOD; |
931 | 284 | } |
932 | | |
933 | | static UA_StatusCode |
934 | | UDP_registerListenSockets(UA_POSIXConnectionManager *pcm, const char *hostname, |
935 | | UA_UInt16 port, const UA_KeyValueMap *params, |
936 | | void *application, void *context, |
937 | | UA_ConnectionManager_connectionCallback connectionCallback, |
938 | 284 | UA_Boolean validate) { |
939 | 284 | UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex); |
940 | | |
941 | | /* Get all the interface and IPv4/6 combinations for the configured hostname */ |
942 | 284 | struct addrinfo hints, *res; |
943 | 284 | memset(&hints, 0, sizeof hints); |
944 | 284 | #if UA_IPV6 |
945 | 284 | hints.ai_family = AF_UNSPEC; /* Allow IPv4 and IPv6 */ |
946 | | #else |
947 | | hints.ai_family = AF_INET; /* IPv4 only */ |
948 | | #endif |
949 | 284 | hints.ai_socktype = SOCK_DGRAM; |
950 | 284 | hints.ai_protocol = IPPROTO_UDP; |
951 | 284 | hints.ai_flags = AI_PASSIVE; |
952 | | |
953 | | /* Set up the port string */ |
954 | 284 | char portstr[6]; |
955 | 284 | mp_snprintf(portstr, 6, "%d", port); |
956 | | |
957 | 284 | UA_RESET_ERRNO; |
958 | 284 | int retcode = UA_getaddrinfo(hostname, portstr, &hints, &res); |
959 | 284 | if(retcode != 0) { |
960 | | #ifdef UA_ARCHITECTURE_WIN32 |
961 | | UA_LOG_SOCKET_ERRNO_GAI_WRAP( |
962 | | UA_LOG_WARNING(pcm->cm.eventSource.eventLoop->logger, |
963 | | UA_LOGCATEGORY_NETWORK, |
964 | | "UDP\t| getaddrinfo lookup for \"%s\" on port %u failed (%s)", |
965 | | hostname, port, errno_str)); |
966 | | #else |
967 | 0 | UA_LOG_WARNING(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK, |
968 | 0 | "UDP\t| getaddrinfo lookup for \"%s\" on port %u failed (%s)", |
969 | 0 | hostname, port, gai_strerror(retcode)); |
970 | 0 | #endif |
971 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
972 | 0 | } |
973 | | |
974 | | /* Add listen sockets */ |
975 | 284 | struct addrinfo *ai = res; |
976 | 284 | UA_StatusCode rv = UA_STATUSCODE_GOOD; |
977 | 568 | while(ai) { |
978 | 284 | rv = UDP_registerListenSocket(pcm, port, ai, params, application, |
979 | 284 | context, connectionCallback, validate); |
980 | 284 | if(rv != UA_STATUSCODE_GOOD) |
981 | 0 | break; |
982 | 284 | ai = ai->ai_next; |
983 | 284 | } |
984 | 284 | UA_freeaddrinfo(res); |
985 | 284 | return rv; |
986 | 284 | } |
987 | | |
988 | | /* Close the connection via a delayed callback */ |
989 | | static void |
990 | 568 | UDP_shutdown(UA_ConnectionManager *cm, UA_RegisteredFD *rfd) { |
991 | 568 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)cm->eventSource.eventLoop; |
992 | 568 | UA_LOCK_ASSERT(&el->elMutex); |
993 | | |
994 | 568 | if(rfd->dc.callback) { |
995 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
996 | 0 | "UDP %u\t| Cannot close - already closing", |
997 | 0 | (unsigned)rfd->fd); |
998 | 0 | return; |
999 | 0 | } |
1000 | | |
1001 | | /* Shutdown the socket to cancel the current select/epoll */ |
1002 | 568 | UA_shutdown(rfd->fd, UA_SHUT_RDWR); |
1003 | | |
1004 | 568 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1005 | 568 | "UDP %u\t| Shutdown called", (unsigned)rfd->fd); |
1006 | | |
1007 | 568 | UA_DelayedCallback *dc = &rfd->dc; |
1008 | 568 | dc->callback = UDP_delayedClose; |
1009 | 568 | dc->application = cm; |
1010 | 568 | dc->context = rfd; |
1011 | | |
1012 | | /* Adding a delayed callback does not take a lock */ |
1013 | 568 | UA_EventLoopPOSIX_addDelayedCallback((UA_EventLoop*)el, dc); |
1014 | 568 | } |
1015 | | |
1016 | | static UA_StatusCode |
1017 | 568 | UDP_shutdownConnection(UA_ConnectionManager *cm, uintptr_t connectionId) { |
1018 | 568 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1019 | 568 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)cm->eventSource.eventLoop; |
1020 | 568 | UA_FD fd = (UA_FD)connectionId; |
1021 | | |
1022 | 568 | UA_LOCK(&el->elMutex); |
1023 | 568 | UA_RegisteredFD *rfd = ZIP_FIND(UA_FDTree, &pcm->fds, &fd); |
1024 | 568 | if(!rfd) { |
1025 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1026 | 0 | "UDP\t| Cannot close UDP connection %u - not found", |
1027 | 0 | (unsigned)connectionId); |
1028 | 0 | UA_UNLOCK(&el->elMutex); |
1029 | 0 | return UA_STATUSCODE_BADNOTFOUND; |
1030 | 0 | } |
1031 | 568 | UDP_shutdown(cm, rfd); |
1032 | 568 | UA_UNLOCK(&el->elMutex); |
1033 | 568 | return UA_STATUSCODE_GOOD; |
1034 | 568 | } |
1035 | | |
1036 | | static UA_StatusCode |
1037 | | UDP_sendWithConnection(UA_ConnectionManager *cm, uintptr_t connectionId, |
1038 | | const UA_KeyValueMap *params, |
1039 | 0 | UA_ByteString *buf) { |
1040 | 0 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1041 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop; |
1042 | |
|
1043 | 0 | UA_LOCK(&el->elMutex); |
1044 | | |
1045 | | /* Look up the registered UDP socket */ |
1046 | 0 | UA_FD fd = (UA_FD)connectionId; |
1047 | 0 | UDP_FD *conn = (UDP_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd); |
1048 | 0 | if(!conn) { |
1049 | 0 | UA_UNLOCK(&el->elMutex); |
1050 | 0 | UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf); |
1051 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1052 | 0 | } |
1053 | | |
1054 | | /* Send the full buffer. This may require several calls to send */ |
1055 | 0 | size_t nWritten = 0; |
1056 | 0 | do { |
1057 | 0 | ssize_t n = 0; |
1058 | 0 | do { |
1059 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1060 | 0 | "UDP %u\t| Attempting to send", (unsigned)connectionId); |
1061 | | |
1062 | | /* Prevent OS signals when sending to a closed socket */ |
1063 | 0 | UA_RESET_ERRNO; |
1064 | 0 | int flags = MSG_NOSIGNAL; |
1065 | 0 | size_t bytes_to_send = buf->length - nWritten; |
1066 | 0 | n = UA_sendto((UA_FD)connectionId, (const char*)buf->data + nWritten, |
1067 | 0 | bytes_to_send, flags, (struct sockaddr*)&conn->sendAddr, |
1068 | 0 | conn->sendAddrLength); |
1069 | 0 | if(n < 0) { |
1070 | | /* An error we cannot recover from? */ |
1071 | 0 | if(UA_ERRNO != UA_INTERRUPTED && |
1072 | 0 | UA_ERRNO != UA_WOULDBLOCK && |
1073 | 0 | UA_ERRNO != UA_AGAIN) { |
1074 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
1075 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1076 | 0 | "UDP %u\t| Send failed with error %s", |
1077 | 0 | (unsigned)connectionId, errno_str)); |
1078 | 0 | UA_UNLOCK(&el->elMutex); |
1079 | 0 | UDP_shutdownConnection(cm, connectionId); |
1080 | 0 | UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf); |
1081 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
1082 | 0 | } |
1083 | | |
1084 | | /* Poll for the socket resources to become available and retry |
1085 | | * (blocking) */ |
1086 | 0 | int poll_ret; |
1087 | 0 | struct pollfd tmp_poll_fd; |
1088 | 0 | tmp_poll_fd.fd = (UA_FD)connectionId; |
1089 | 0 | tmp_poll_fd.events = UA_POLLOUT; |
1090 | 0 | do { |
1091 | 0 | UA_RESET_ERRNO; |
1092 | 0 | poll_ret = UA_poll(&tmp_poll_fd, 1, 100); |
1093 | 0 | if(poll_ret < 0 && UA_ERRNO != UA_INTERRUPTED) { |
1094 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
1095 | 0 | UA_LOG_ERROR(el->eventLoop.logger, |
1096 | 0 | UA_LOGCATEGORY_NETWORK, |
1097 | 0 | "UDP %u\t| Send failed with error %s", |
1098 | 0 | (unsigned)connectionId, errno_str)); |
1099 | 0 | UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf); |
1100 | 0 | UDP_shutdown(cm, &conn->rfd); |
1101 | 0 | UA_UNLOCK(&el->elMutex); |
1102 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
1103 | 0 | } |
1104 | 0 | } while(poll_ret <= 0); |
1105 | 0 | } |
1106 | 0 | } while(n < 0); |
1107 | 0 | nWritten += (size_t)n; |
1108 | 0 | } while(nWritten < buf->length); |
1109 | | |
1110 | | /* Free the buffer */ |
1111 | 0 | UA_UNLOCK(&el->elMutex); |
1112 | 0 | UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf); |
1113 | 0 | return UA_STATUSCODE_GOOD; |
1114 | 0 | } |
1115 | | |
1116 | | static UA_StatusCode |
1117 | | registerSocketAndDestinationForSend(const UA_KeyValueMap *params, |
1118 | | const char *hostname, struct addrinfo *info, |
1119 | | int error, UDP_FD *ufd, UA_FD *sock, |
1120 | 284 | const UA_Logger *logger, UA_Boolean validate) { |
1121 | 284 | UA_RESET_ERRNO; |
1122 | 284 | UA_FD newSock = UA_socket(info->ai_family, info->ai_socktype, info->ai_protocol); |
1123 | 284 | *sock = newSock; |
1124 | 284 | if(newSock == UA_INVALID_FD) { |
1125 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
1126 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, |
1127 | 0 | "UDP\t| Could not create socket to connect to %s (%s)", |
1128 | 0 | hostname, errno_str)); |
1129 | 0 | return UA_STATUSCODE_BADDISCONNECT; |
1130 | 0 | } |
1131 | 284 | UA_StatusCode res = setConnectionConfig(newSock, params, info->ai_family, logger); |
1132 | 284 | if(res != UA_STATUSCODE_GOOD) { |
1133 | 0 | UA_close(newSock); |
1134 | 0 | return res; |
1135 | 0 | } |
1136 | | |
1137 | | /* Prepare socket for multicast */ |
1138 | 284 | MultiCastType mc = multiCastType(info); |
1139 | 284 | if(mc != MULTICASTTYPE_NONE) { |
1140 | 284 | res = setupSendMultiCast(newSock, info, params, mc, (validate) ? NULL : logger); |
1141 | 284 | if(res != UA_STATUSCODE_GOOD) { |
1142 | 0 | UA_close(newSock); |
1143 | 0 | return res; |
1144 | 0 | } |
1145 | 284 | } |
1146 | | |
1147 | 284 | memcpy(&ufd->sendAddr, info->ai_addr, info->ai_addrlen); |
1148 | 284 | ufd->sendAddrLength = info->ai_addrlen; |
1149 | 284 | return UA_STATUSCODE_GOOD; |
1150 | 284 | } |
1151 | | |
1152 | | static UA_StatusCode |
1153 | | UDP_openSendConnection(UA_POSIXConnectionManager *pcm, const UA_KeyValueMap *params, |
1154 | | void *application, void *context, |
1155 | | UA_ConnectionManager_connectionCallback connectionCallback, |
1156 | 284 | UA_Boolean validate) { |
1157 | 284 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)pcm->cm.eventSource.eventLoop; |
1158 | 284 | UA_LOCK_ASSERT(&el->elMutex); |
1159 | | |
1160 | | /* Get the connection parameters */ |
1161 | 284 | char hostname[UA_MAXHOSTNAME_LENGTH]; |
1162 | 284 | char portStr[UA_MAXPORTSTR_LENGTH]; |
1163 | 284 | struct addrinfo *info = NULL; |
1164 | | |
1165 | 284 | int error = getConnectionInfoFromParams(params, hostname, |
1166 | 284 | portStr, &info, el->eventLoop.logger); |
1167 | 284 | if(error < 0 || info == NULL) { |
1168 | 0 | if(info != NULL) { |
1169 | 0 | UA_freeaddrinfo(info); |
1170 | 0 | } |
1171 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1172 | 0 | "UDP\t| Opening a connection failed"); |
1173 | 0 | return UA_STATUSCODE_BADCONNECTIONREJECTED; |
1174 | 0 | } |
1175 | | |
1176 | 284 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1177 | 284 | "UDP\t| Open a connection to \"%s\" on port %s", hostname, portStr); |
1178 | | |
1179 | | /* Allocate the UA_RegisteredFD */ |
1180 | 284 | UDP_FD *conn = (UDP_FD*)UA_calloc(1, sizeof(UDP_FD)); |
1181 | 284 | if(!conn) { |
1182 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1183 | 0 | "UDP\t| Error allocating memory for the socket, closing"); |
1184 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
1185 | 0 | } |
1186 | | |
1187 | | /* Create a socket and register the destination address from the provided parameters */ |
1188 | 284 | UA_RESET_ERRNO; |
1189 | 284 | UA_FD newSock = UA_INVALID_FD; |
1190 | 284 | UA_StatusCode res = |
1191 | 284 | registerSocketAndDestinationForSend(params, hostname, info, |
1192 | 284 | error, conn, &newSock, |
1193 | 284 | el->eventLoop.logger, validate); |
1194 | 284 | UA_freeaddrinfo(info); |
1195 | 284 | if(validate && res == UA_STATUSCODE_GOOD) { |
1196 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1197 | 0 | "UDP\t| Connection parameters to \"%s\" on port %s have been validated", |
1198 | 0 | hostname, portStr); |
1199 | 0 | UA_close(newSock); |
1200 | 0 | UA_free(conn); |
1201 | 0 | return UA_STATUSCODE_GOOD; |
1202 | 0 | } |
1203 | 284 | if(res != UA_STATUSCODE_GOOD) { |
1204 | 0 | if(!validate) { |
1205 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
1206 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1207 | 0 | "UDP\t| Connection to \"%s\" on port %s could not be opened " |
1208 | 0 | "with error: %s", hostname, portStr, errno_str)); |
1209 | 0 | } else { |
1210 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
1211 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1212 | 0 | "UDP\t| Invalid connection parameters to \"%s\" on " |
1213 | 0 | "port %s (Error: %s)", hostname, portStr, errno_str)); |
1214 | 0 | } |
1215 | 0 | UA_free(conn); |
1216 | 0 | return res; |
1217 | 0 | } |
1218 | | |
1219 | 284 | conn->rfd.fd = newSock; |
1220 | 284 | conn->rfd.listenEvents = 0; |
1221 | 284 | conn->rfd.es = &pcm->cm.eventSource; |
1222 | 284 | conn->rfd.eventSourceCB = (UA_FDCallback)UDP_connectionSocketCallback; |
1223 | 284 | conn->applicationCB = connectionCallback; |
1224 | 284 | conn->application = application; |
1225 | 284 | conn->context = context; |
1226 | | |
1227 | | /* Register the fd to trigger when output is possible (the connection is open) */ |
1228 | 284 | res = UA_EventLoopPOSIX_registerFD(el, &conn->rfd); |
1229 | 284 | if(res != UA_STATUSCODE_GOOD) { |
1230 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1231 | 0 | "UDP\t| Registering the socket for %s failed", hostname); |
1232 | 0 | UA_close(newSock); |
1233 | 0 | UA_free(conn); |
1234 | 0 | return res; |
1235 | 0 | } |
1236 | | |
1237 | | /* Register internally in the EventSource */ |
1238 | 284 | ZIP_INSERT(UA_FDTree, &pcm->fds, &conn->rfd); |
1239 | 284 | pcm->fdsSize++; |
1240 | | |
1241 | 284 | UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1242 | 284 | "UDP %u\t| New connection to \"%s\" on port %s", |
1243 | 284 | (unsigned)newSock, hostname, portStr); |
1244 | | |
1245 | | /* Signal the connection as opening. The connection fully opens in the next |
1246 | | * iteration of the EventLoop */ |
1247 | 284 | connectionCallback(&pcm->cm, (uintptr_t)newSock, application, |
1248 | 284 | &conn->context, UA_CONNECTIONSTATE_ESTABLISHED, |
1249 | 284 | &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL); |
1250 | | |
1251 | 284 | return UA_STATUSCODE_GOOD; |
1252 | 284 | } |
1253 | | |
1254 | | static UA_StatusCode |
1255 | | UDP_openReceiveConnection(UA_POSIXConnectionManager *pcm, const UA_KeyValueMap *params, |
1256 | | void *application, void *context, |
1257 | | UA_ConnectionManager_connectionCallback connectionCallback, |
1258 | 284 | UA_Boolean validate) { |
1259 | 284 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop; |
1260 | 284 | UA_LOCK_ASSERT(&el->elMutex); |
1261 | | |
1262 | | /* Get the port */ |
1263 | 284 | const UA_UInt16 *port = (const UA_UInt16*) |
1264 | 284 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_PORT].name, |
1265 | 284 | &UA_TYPES[UA_TYPES_UINT16]); |
1266 | 284 | UA_assert(port); /* checked before */ |
1267 | | |
1268 | | /* Get the hostname configuration */ |
1269 | 284 | const UA_Variant *addrs = |
1270 | 284 | UA_KeyValueMap_get(params, udpConnectionParams[UDP_PARAMINDEX_ADDR].name); |
1271 | 284 | size_t addrsSize = 0; |
1272 | 284 | if(addrs) { |
1273 | 284 | UA_assert(addrs->type == &UA_TYPES[UA_TYPES_STRING]); |
1274 | 284 | if(UA_Variant_isScalar(addrs)) |
1275 | 284 | addrsSize = 1; |
1276 | 0 | else |
1277 | 0 | addrsSize = addrs->arrayLength; |
1278 | 284 | } |
1279 | | |
1280 | | /* No hostname configured -> listen on all interfaces */ |
1281 | 284 | if(addrsSize == 0) { |
1282 | 0 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1283 | 0 | "UDP\t| Listening on all interfaces"); |
1284 | 0 | return UDP_registerListenSockets(pcm, NULL, *port, params, application, |
1285 | 0 | context, connectionCallback, validate); |
1286 | 0 | } |
1287 | | |
1288 | | /* Iterate over the configured hostnames */ |
1289 | 284 | UA_String *hostStrings = (UA_String*)addrs->data; |
1290 | 568 | for(size_t i = 0; i < addrsSize; i++) { |
1291 | 284 | char hn[UA_MAXHOSTNAME_LENGTH]; |
1292 | 284 | if(hostStrings[i].length >= sizeof(hn)) |
1293 | 0 | continue; |
1294 | 284 | memcpy(hn, hostStrings[i].data, hostStrings->length); |
1295 | 284 | hn[hostStrings->length] = '\0'; |
1296 | 284 | UA_StatusCode rv = |
1297 | 284 | UDP_registerListenSockets(pcm, hn, *port, params, application, |
1298 | 284 | context, connectionCallback, validate); |
1299 | 284 | if(rv != UA_STATUSCODE_GOOD) |
1300 | 0 | return rv; |
1301 | 284 | } |
1302 | | |
1303 | 284 | return UA_STATUSCODE_GOOD; |
1304 | 284 | } |
1305 | | |
1306 | | static UA_StatusCode |
1307 | | UDP_openConnection(UA_ConnectionManager *cm, const UA_KeyValueMap *params, |
1308 | | void *application, void *context, |
1309 | 568 | UA_ConnectionManager_connectionCallback connectionCallback) { |
1310 | 568 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1311 | 568 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop; |
1312 | 568 | UA_LOCK(&el->elMutex); |
1313 | | |
1314 | 568 | if(cm->eventSource.state != UA_EVENTSOURCESTATE_STARTED) { |
1315 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1316 | 0 | "UDP\t| Cannot open a connection for a " |
1317 | 0 | "ConnectionManager that is not started"); |
1318 | 0 | UA_UNLOCK(&el->elMutex); |
1319 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1320 | 0 | } |
1321 | | |
1322 | | /* Check the parameters */ |
1323 | 568 | UA_StatusCode res = |
1324 | 568 | UA_KeyValueRestriction_validate(el->eventLoop.logger, "UDP", |
1325 | 568 | udpConnectionParams, |
1326 | 568 | UDP_PARAMETERSSIZE, params); |
1327 | 568 | if(res != UA_STATUSCODE_GOOD) { |
1328 | 0 | UA_UNLOCK(&el->elMutex); |
1329 | 0 | return res; |
1330 | 0 | } |
1331 | | |
1332 | 568 | UA_Boolean validate = false; |
1333 | 568 | const UA_Boolean *validationValue = (const UA_Boolean*) |
1334 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_VALIDATE].name, |
1335 | 568 | &UA_TYPES[UA_TYPES_BOOLEAN]); |
1336 | 568 | if(validationValue) |
1337 | 0 | validate = *validationValue; |
1338 | | |
1339 | 568 | UA_Boolean listen = false; |
1340 | 568 | const UA_Boolean *listenValue = (const UA_Boolean*) |
1341 | 568 | UA_KeyValueMap_getScalar(params, udpConnectionParams[UDP_PARAMINDEX_LISTEN].name, |
1342 | 568 | &UA_TYPES[UA_TYPES_BOOLEAN]); |
1343 | 568 | if(listenValue) |
1344 | 568 | listen = *listenValue; |
1345 | | |
1346 | 568 | if(listen) { |
1347 | 284 | res = UDP_openReceiveConnection(pcm, params, application, context, |
1348 | 284 | connectionCallback, validate); |
1349 | 284 | } else { |
1350 | 284 | res = UDP_openSendConnection(pcm, params, application, context, |
1351 | 284 | connectionCallback, validate); |
1352 | 284 | } |
1353 | 568 | UA_UNLOCK(&el->elMutex); |
1354 | 568 | return res; |
1355 | 568 | } |
1356 | | |
1357 | | static UA_StatusCode |
1358 | 547 | UDP_eventSourceStart(UA_ConnectionManager *cm) { |
1359 | 547 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1360 | 547 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop; |
1361 | 547 | if(!el) |
1362 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1363 | | |
1364 | 547 | UA_LOCK(&el->elMutex); |
1365 | | |
1366 | | /* Check the state */ |
1367 | 547 | if(cm->eventSource.state != UA_EVENTSOURCESTATE_STOPPED) { |
1368 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1369 | 0 | "UDP\t| To start the ConnectionManager, " |
1370 | 0 | "it has to be registered in an EventLoop and not started"); |
1371 | 0 | UA_UNLOCK(&el->elMutex); |
1372 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1373 | 0 | } |
1374 | | |
1375 | | /* Check the parameters */ |
1376 | 547 | UA_StatusCode res = |
1377 | 547 | UA_KeyValueRestriction_validate(el->eventLoop.logger, "UDP", |
1378 | 547 | udpManagerParams, UDP_MANAGERPARAMS, |
1379 | 547 | &cm->eventSource.params); |
1380 | 547 | if(res != UA_STATUSCODE_GOOD) |
1381 | 0 | goto finish; |
1382 | | |
1383 | | /* Allocate the rx buffer */ |
1384 | 547 | res = UA_EventLoopPOSIX_allocateStaticBuffers(pcm); |
1385 | 547 | if(res != UA_STATUSCODE_GOOD) |
1386 | 0 | goto finish; |
1387 | | |
1388 | | /* Set the EventSource to the started state */ |
1389 | 547 | cm->eventSource.state = UA_EVENTSOURCESTATE_STARTED; |
1390 | | |
1391 | 547 | finish: |
1392 | 547 | UA_UNLOCK(&el->elMutex); |
1393 | 547 | return res; |
1394 | 547 | } |
1395 | | |
1396 | | static void * |
1397 | 0 | UDP_shutdownCB(void *application, UA_RegisteredFD *rfd) { |
1398 | 0 | UA_ConnectionManager *cm = (UA_ConnectionManager*)application; |
1399 | 0 | UDP_shutdown(cm, rfd); |
1400 | 0 | return NULL; |
1401 | 0 | } |
1402 | | |
1403 | | static void |
1404 | 547 | UDP_eventSourceStop(UA_ConnectionManager *cm) { |
1405 | 547 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1406 | 547 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop; |
1407 | 547 | (void)el; |
1408 | 547 | UA_LOCK(&el->elMutex); |
1409 | | |
1410 | 547 | UA_LOG_DEBUG(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK, |
1411 | 547 | "UDP\t| Shutting down the ConnectionManager"); |
1412 | | |
1413 | | /* Prevent new connections to open */ |
1414 | 547 | cm->eventSource.state = UA_EVENTSOURCESTATE_STOPPING; |
1415 | | |
1416 | | /* Shutdown all existing connection */ |
1417 | 547 | ZIP_ITER(UA_FDTree, &pcm->fds, UDP_shutdownCB, cm); |
1418 | | |
1419 | | /* Check if stopped once more (also checking inside UDP_close, but there we |
1420 | | * don't check if there is no rfd at all) */ |
1421 | 547 | UDP_checkStopped(pcm); |
1422 | | |
1423 | 547 | UA_UNLOCK(&el->elMutex); |
1424 | 547 | } |
1425 | | |
1426 | | static UA_StatusCode |
1427 | 547 | UDP_eventSourceDelete(UA_ConnectionManager *cm) { |
1428 | 547 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
1429 | 547 | if(cm->eventSource.state >= UA_EVENTSOURCESTATE_STARTING) { |
1430 | 0 | UA_LOG_ERROR(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
1431 | 0 | "UDP\t| The EventSource must be stopped before it can be deleted"); |
1432 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1433 | 0 | } |
1434 | | |
1435 | 547 | UA_ByteString_clear(&pcm->rxBuffer); |
1436 | 547 | UA_ByteString_clear(&pcm->txBuffer); |
1437 | 547 | UA_KeyValueMap_clear(&cm->eventSource.params); |
1438 | 547 | UA_String_clear(&cm->eventSource.name); |
1439 | 547 | UA_free(cm); |
1440 | | |
1441 | 547 | return UA_STATUSCODE_GOOD; |
1442 | 547 | } |
1443 | | |
1444 | | static const char *udpName = "udp"; |
1445 | | |
1446 | | UA_ConnectionManager * |
1447 | 547 | UA_ConnectionManager_new_POSIX_UDP(const UA_String eventSourceName) { |
1448 | 547 | UA_POSIXConnectionManager *cm = (UA_POSIXConnectionManager*) |
1449 | 547 | UA_calloc(1, sizeof(UA_POSIXConnectionManager)); |
1450 | 547 | if(!cm) |
1451 | 0 | return NULL; |
1452 | | |
1453 | 547 | cm->cm.eventSource.eventSourceType = UA_EVENTSOURCETYPE_CONNECTIONMANAGER; |
1454 | 547 | UA_String_copy(&eventSourceName, &cm->cm.eventSource.name); |
1455 | 547 | cm->cm.eventSource.start = (UA_StatusCode (*)(UA_EventSource *))UDP_eventSourceStart; |
1456 | 547 | cm->cm.eventSource.stop = (void (*)(UA_EventSource *))UDP_eventSourceStop; |
1457 | 547 | cm->cm.eventSource.free = (UA_StatusCode (*)(UA_EventSource *))UDP_eventSourceDelete; |
1458 | 547 | cm->cm.protocol = UA_STRING((char*)(uintptr_t)udpName); |
1459 | 547 | cm->cm.openConnection = UDP_openConnection; |
1460 | 547 | cm->cm.allocNetworkBuffer = UA_EventLoopPOSIX_allocNetworkBuffer; |
1461 | 547 | cm->cm.freeNetworkBuffer = UA_EventLoopPOSIX_freeNetworkBuffer; |
1462 | 547 | cm->cm.sendWithConnection = UDP_sendWithConnection; |
1463 | 547 | cm->cm.closeConnection = UDP_shutdownConnection; |
1464 | 547 | return &cm->cm; |
1465 | 547 | } |
1466 | | |
1467 | | #endif |