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