Coverage Report

Created: 2026-06-30 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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