Coverage Report

Created: 2025-08-26 06:30

/src/open62541/arch/posix/eventloop_posix_tcp.c
Line
Count
Source (jump to first uncovered line)
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 "open62541/types.h"
10
#include "eventloop_posix.h"
11
12
#if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) || defined(UA_ARCHITECTURE_WIN32)
13
14
/* Configuration parameters */
15
551
#define TCP_MANAGERPARAMS 2
16
17
static UA_KeyValueRestriction tcpManagerParams[TCP_MANAGERPARAMS] = {
18
    {{0, UA_STRING_STATIC("recv-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false},
19
    {{0, UA_STRING_STATIC("send-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false}
20
};
21
22
300
#define TCP_PARAMETERSSIZE 5
23
300
#define TCP_PARAMINDEX_ADDR 0
24
300
#define TCP_PARAMINDEX_PORT 1
25
300
#define TCP_PARAMINDEX_LISTEN 2
26
300
#define TCP_PARAMINDEX_VALIDATE 3
27
300
#define TCP_PARAMINDEX_REUSE 4
28
29
static UA_KeyValueRestriction tcpConnectionParams[TCP_PARAMETERSSIZE] = {
30
    {{0, UA_STRING_STATIC("address")}, &UA_TYPES[UA_TYPES_STRING], false, true, true},
31
    {{0, UA_STRING_STATIC("port")}, &UA_TYPES[UA_TYPES_UINT16], true, true, false},
32
    {{0, UA_STRING_STATIC("listen")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
33
    {{0, UA_STRING_STATIC("validate")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
34
    {{0, UA_STRING_STATIC("reuse")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false}
35
};
36
37
typedef struct {
38
    UA_RegisteredFD rfd;
39
40
    UA_ConnectionManager_connectionCallback applicationCB;
41
    void *application;
42
    void *context;
43
} TCP_FD;
44
45
static void
46
TCP_shutdown(UA_ConnectionManager *cm, TCP_FD *conn);
47
48
/* Do not merge packets on the socket (disable Nagle's algorithm) */
49
static UA_StatusCode
50
83
TCP_setNoNagle(UA_FD sockfd) {
51
83
    int val = 1;
52
83
    int res = UA_setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
53
83
    if(res < 0)
54
0
        return UA_STATUSCODE_BADINTERNALERROR;
55
83
    return UA_STATUSCODE_GOOD;
56
83
}
57
58
/* Test if the ConnectionManager can be stopped */
59
static void
60
1.22k
TCP_checkStopped(UA_POSIXConnectionManager *pcm) {
61
1.22k
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
62
63
1.22k
    if(pcm->fdsSize == 0 &&
64
1.22k
       pcm->cm.eventSource.state == UA_EVENTSOURCESTATE_STOPPING) {
65
551
        UA_LOG_DEBUG(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
66
551
                     "TCP\t| All sockets closed, the EventLoop has stopped");
67
551
        pcm->cm.eventSource.state = UA_EVENTSOURCESTATE_STOPPED;
68
551
    }
69
1.22k
}
70
71
static void
72
677
TCP_delayedClose(void *application, void *context) {
73
677
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application;
74
677
    UA_ConnectionManager *cm = &pcm->cm;
75
677
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
76
677
    TCP_FD *conn = (TCP_FD*)context;
77
78
677
    UA_LOCK(&el->elMutex);
79
80
677
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
81
677
                 "TCP %u\t| Delayed closing of the connection",
82
677
                 (unsigned)conn->rfd.fd);
83
84
    /* Ensure reuse is possible right away. Port-stealing is no longer an issue
85
     * as the socket gets closed anyway. And we do not want to wait for the
86
     * timeout to open a new socket for the same address and port. */
87
677
    UA_EventLoopPOSIX_setReusable(conn->rfd.fd);
88
89
    /* Deregister from the EventLoop */
90
677
    UA_EventLoopPOSIX_deregisterFD(el, &conn->rfd);
91
92
    /* Deregister internally */
93
677
    ZIP_REMOVE(UA_FDTree, &pcm->fds, &conn->rfd);
94
677
    UA_assert(pcm->fdsSize > 0);
95
677
    pcm->fdsSize--;
96
97
    /* Signal closing to the application */
98
677
    conn->applicationCB(cm, (uintptr_t)conn->rfd.fd,
99
677
                        conn->application, &conn->context,
100
677
                        UA_CONNECTIONSTATE_CLOSING,
101
677
                        &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
102
103
    /* Close the socket */
104
677
    UA_RESET_ERRNO;
105
677
    int ret = UA_close(conn->rfd.fd);
106
677
    if(ret == 0) {
107
677
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
108
677
                    "TCP %u\t| Socket closed", (unsigned)conn->rfd.fd);
109
677
    } else {
110
0
        UA_LOG_SOCKET_ERRNO_WRAP(
111
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
112
0
                          "TCP %u\t| Could not close the socket (%s)",
113
0
                          (unsigned)conn->rfd.fd, errno_str));
114
0
    }
115
116
677
    UA_free(conn);
117
118
    /* Check if this was the last connection for a closing ConnectionManager */
119
677
    TCP_checkStopped(pcm);
120
121
677
    UA_UNLOCK(&el->elMutex);
122
677
}
123
124
static int
125
0
getSockError(TCP_FD *conn) {
126
0
    int error = 0;
127
0
#ifndef UA_ARCHITECTURE_WIN32
128
0
    socklen_t errlen = sizeof(int);
129
0
    int err = UA_getsockopt(conn->rfd.fd, SOL_SOCKET, SO_ERROR, &error, &errlen);
130
#else
131
    int errlen = (int)sizeof(int);
132
    int err = UA_getsockopt((SOCKET)conn->rfd.fd, SOL_SOCKET, SO_ERROR,
133
                         (char*)&error, &errlen);
134
#endif
135
0
    return (err == 0) ? error : err;
136
0
}
137
138
/* Gets called when a connection socket opens, receives data or closes */
139
static void
140
TCP_connectionSocketCallback(UA_ConnectionManager *cm, TCP_FD *conn,
141
64
                             short event) {
142
64
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
143
64
    UA_LOCK_ASSERT(&el->elMutex);
144
145
64
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
146
64
                 "TCP %u\t| Activity on the socket",
147
64
                 (unsigned)conn->rfd.fd);
148
149
    /* Error. The connection has closed. */
150
64
    if(event == UA_FDEVENT_ERR) {
151
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
152
0
                    "TCP %u\t| The connection closes with error %i",
153
0
                    (unsigned)conn->rfd.fd, getSockError(conn));
154
0
        TCP_shutdown(cm, conn);
155
0
        return;
156
0
    }
157
158
    /* Write-Event, a new connection has opened. But some errors come as an
159
     * out-event. For example if the remote side could not be reached to
160
     * initiate the connection. So we check manually for error conditions on
161
     * the socket. */
162
64
    if(event == UA_FDEVENT_OUT) {
163
0
        int error = getSockError(conn);
164
0
        if(error != 0) {
165
0
            UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
166
0
                        "TCP %u\t| The connection closes with error %i",
167
0
                        (unsigned)conn->rfd.fd, error);
168
0
            TCP_shutdown(cm, conn);
169
0
            return;
170
0
        }
171
172
0
        UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
173
0
                     "TCP %u\t| Opening a new connection",
174
0
                     (unsigned)conn->rfd.fd);
175
176
        /* Now we are interested in read-events. */
177
0
        conn->rfd.listenEvents = UA_FDEVENT_IN;
178
0
        UA_EventLoopPOSIX_modifyFD(el, &conn->rfd);
179
180
        /* A new socket has opened. Signal it to the application. */
181
0
        conn->applicationCB(cm, (uintptr_t)conn->rfd.fd,
182
0
                            conn->application, &conn->context,
183
0
                            UA_CONNECTIONSTATE_ESTABLISHED,
184
0
                            &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
185
0
        return;
186
0
    }
187
188
64
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
189
64
                 "TCP %u\t| Allocate receive buffer",
190
64
                 (unsigned)conn->rfd.fd);
191
192
    /* Use the already allocated receive-buffer */
193
64
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
194
64
    UA_ByteString response = pcm->rxBuffer;
195
196
    /* Receive */
197
64
    UA_RESET_ERRNO;
198
64
#ifndef UA_ARCHITECTURE_WIN32
199
64
    ssize_t ret = UA_recv(conn->rfd.fd, (char*)response.data,
200
64
                          response.length, MSG_DONTWAIT);
201
#else
202
    int ret = UA_recv(conn->rfd.fd, (char*)response.data,
203
                      response.length, MSG_DONTWAIT);
204
#endif
205
206
    /* Receive has failed */
207
64
    if(ret <= 0) {
208
0
        if(UA_ERRNO == UA_INTERRUPTED ||
209
0
           UA_ERRNO == UA_WOULDBLOCK ||
210
0
           UA_ERRNO == UA_AGAIN)
211
0
            return; /* Temporary error on an non-blocking socket */
212
213
        /* Orderly shutdown of the socket */
214
0
        UA_LOG_SOCKET_ERRNO_WRAP(
215
0
           UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
216
0
                        "TCP %u\t| recv signaled the socket was shutdown (%s)",
217
0
                        (unsigned)conn->rfd.fd, errno_str));
218
0
        TCP_shutdown(cm, conn);
219
0
        return;
220
0
    }
221
222
64
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
223
64
                 "TCP %u\t| Received message of size %u",
224
64
                 (unsigned)conn->rfd.fd, (unsigned)ret);
225
226
    /* Callback to the application layer */
227
64
    response.length = (size_t)ret; /* Set the length of the received buffer */
228
64
    conn->applicationCB(cm, (uintptr_t)conn->rfd.fd,
229
64
                        conn->application, &conn->context,
230
64
                        UA_CONNECTIONSTATE_ESTABLISHED,
231
64
                        &UA_KEYVALUEMAP_NULL, response);
232
64
}
233
234
/* Gets called when a new connection opens or if the listenSocket is closed */
235
static void
236
83
TCP_listenSocketCallback(UA_ConnectionManager *cm, TCP_FD *conn, short event) {
237
83
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
238
83
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
239
83
    UA_LOCK_ASSERT(&el->elMutex);
240
241
83
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
242
83
                 "TCP %u\t| Callback on server socket",
243
83
                 (unsigned)conn->rfd.fd);
244
245
    /* Try to accept a new connection */
246
83
    UA_RESET_ERRNO;
247
83
    struct sockaddr_storage remote;
248
83
    socklen_t remote_size = sizeof(remote);
249
83
    UA_FD newsockfd = UA_accept(conn->rfd.fd, (struct sockaddr*)&remote, &remote_size);
250
83
    if(newsockfd == UA_INVALID_FD) {
251
        /* Temporary error -- retry */
252
0
        if(UA_IS_TEMPORARY_ACCEPT_ERROR(UA_ERRNO))
253
0
            return;
254
255
        /* Close the listen socket */
256
0
        if(cm->eventSource.state != UA_EVENTSOURCESTATE_STOPPING) {
257
0
            UA_LOG_SOCKET_ERRNO_WRAP(
258
0
                UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
259
0
                               "TCP %u\t| Error %s, closing the server socket",
260
0
                               (unsigned)conn->rfd.fd, errno_str));
261
0
        }
262
263
0
        TCP_shutdown(cm, conn);
264
0
        return;
265
0
    }
266
267
    /* Log the name of the remote host */
268
83
    UA_RESET_ERRNO;
269
83
    char hoststr[UA_MAXHOSTNAME_LENGTH];
270
83
    int get_res = UA_getnameinfo((struct sockaddr *)&remote, sizeof(remote),
271
83
                                 hoststr, sizeof(hoststr),
272
83
                                 NULL, 0, NI_NUMERICHOST);
273
83
    if(get_res != 0) {
274
0
        UA_LOG_SOCKET_ERRNO_WRAP(
275
0
           UA_LOG_WARNING(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
276
0
                          "TCP %u\t| getnameinfo(...) could not resolve the "
277
0
                          "hostname (%s)", (unsigned)conn->rfd.fd, errno_str));
278
0
    }
279
83
    UA_LOG_INFO(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
280
83
                "TCP %u\t| Connection opened from \"%s\" via the server socket %u",
281
83
                (unsigned)newsockfd, hoststr, (unsigned)conn->rfd.fd);
282
283
    /* Configure the new socket */
284
83
    UA_RESET_ERRNO;
285
83
    UA_StatusCode res = UA_STATUSCODE_GOOD;
286
    /* res |= UA_EventLoopPOSIX_setNonBlocking(newsockfd); Inherited from the listen-socket */
287
83
    res |= UA_EventLoopPOSIX_setNoSigPipe(newsockfd); /* Supress interrupts from the socket */
288
83
    res |= TCP_setNoNagle(newsockfd);     /* Disable Nagle's algorithm */
289
83
    if(res != UA_STATUSCODE_GOOD) {
290
0
        UA_LOG_SOCKET_ERRNO_WRAP(
291
0
            UA_LOG_WARNING(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
292
0
                           "TCP %u\t| Error seeting the TCP options (%s)",
293
0
                           (unsigned)newsockfd, errno_str));
294
        /* Close the new socket */
295
0
        UA_close(newsockfd);
296
0
        return;
297
0
    }
298
299
    /* Allocate the UA_RegisteredFD */
300
83
    TCP_FD *newConn = (TCP_FD*)UA_calloc(1, sizeof(TCP_FD));
301
83
    if(!newConn) {
302
6
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
303
6
                       "TCP %u\t| Error allocating memory for the socket",
304
6
                       (unsigned)newsockfd);
305
6
        UA_close(newsockfd);
306
6
        return;
307
6
    }
308
309
77
    newConn->rfd.fd = newsockfd;
310
77
    newConn->rfd.listenEvents = UA_FDEVENT_IN;
311
77
    newConn->rfd.es = &cm->eventSource;
312
77
    newConn->rfd.eventSourceCB = (UA_FDCallback)TCP_connectionSocketCallback;
313
77
    newConn->applicationCB = conn->applicationCB;
314
77
    newConn->application = conn->application;
315
77
    newConn->context = conn->context;
316
317
    /* Register in the EventLoop. Signal to the user if registering failed. */
318
77
    res = UA_EventLoopPOSIX_registerFD(el, &newConn->rfd);
319
77
    if(res != UA_STATUSCODE_GOOD) {
320
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
321
0
                       "TCP %u\t| Error registering the socket",
322
0
                       (unsigned)newsockfd);
323
0
        UA_free(newConn);
324
0
        UA_close(newsockfd);
325
0
        return;
326
0
    }
327
328
    /* Register internally in the EventSource */
329
77
    ZIP_INSERT(UA_FDTree, &pcm->fds, &newConn->rfd);
330
77
    pcm->fdsSize++;
331
332
    /* Forward the remote hostname to the application */
333
77
    UA_KeyValuePair kvp;
334
77
    kvp.key = UA_QUALIFIEDNAME(0, "remote-address");
335
77
    UA_String hostName = UA_STRING(hoststr);
336
77
    UA_Variant_setScalar(&kvp.value, &hostName, &UA_TYPES[UA_TYPES_STRING]);
337
338
77
    UA_KeyValueMap kvm;
339
77
    kvm.mapSize = 1;
340
77
    kvm.map = &kvp;
341
342
    /* The socket has opened. Signal it to the application. */
343
77
    newConn->applicationCB(cm, (uintptr_t)newsockfd,
344
77
                           newConn->application, &newConn->context,
345
77
                           UA_CONNECTIONSTATE_ESTABLISHED,
346
77
                           &kvm, UA_BYTESTRING_NULL);
347
77
}
348
349
static UA_StatusCode
350
TCP_registerListenSocket(UA_POSIXConnectionManager *pcm, struct addrinfo *ai,
351
                         const char *hostname, UA_UInt16 port,
352
                         void *application, void *context,
353
                         UA_ConnectionManager_connectionCallback connectionCallback,
354
600
                         UA_Boolean validate, UA_Boolean reuseaddr) {
355
600
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
356
600
    UA_LOCK_ASSERT(&el->elMutex);
357
358
    /* Translate INADDR_ANY to IPv4/IPv6 address */
359
600
    UA_RESET_ERRNO;
360
600
    char addrstr[UA_MAXHOSTNAME_LENGTH];
361
600
    int get_res = UA_getnameinfo(ai->ai_addr, ai->ai_addrlen,
362
600
                                 addrstr, sizeof(addrstr), NULL, 0, 0);
363
600
    if(get_res != 0) {
364
0
        get_res = UA_getnameinfo(ai->ai_addr, ai->ai_addrlen,
365
0
                                 addrstr, sizeof(addrstr),
366
0
                                 NULL, 0, NI_NUMERICHOST);
367
0
        if(get_res != 0) {
368
0
            addrstr[0] = 0;
369
0
            UA_LOG_SOCKET_ERRNO_WRAP(
370
0
                UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
371
0
                               "TCP\t| getnameinfo(...) could not resolve the "
372
0
                               "hostname (%s)", errno_str));
373
0
        }
374
0
    }
375
376
    /* Create the server socket */
377
600
    UA_RESET_ERRNO;
378
600
    UA_FD listenSocket = UA_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
379
600
    if(listenSocket == UA_INVALID_FD) {
380
0
        UA_LOG_SOCKET_ERRNO_WRAP(
381
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
382
0
                          "TCP %u\t| Error opening the listen socket for "
383
0
                          "\"%s\" on port %u (%s)",
384
0
                          (unsigned)listenSocket, addrstr, port, errno_str));
385
0
        return UA_STATUSCODE_BADINTERNALERROR;
386
0
    }
387
388
    /* Some Linux distributions have net.ipv6.bindv6only not activated. So
389
     * sockets can double-bind to IPv4 and IPv6. This leads to problems. Use
390
     * AF_INET6 sockets only for IPv6. */
391
600
#if UA_IPV6
392
600
    int optval = 1;
393
600
    if(ai->ai_family == AF_INET6 &&
394
600
       UA_setsockopt(listenSocket, IPPROTO_IPV6, IPV6_V6ONLY,
395
300
                     (const char*)&optval, sizeof(optval)) == -1) {
396
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
397
0
                       "TCP %u\t| Could not set an IPv6 socket to IPv6 only",
398
0
                       (unsigned)listenSocket);
399
0
        UA_close(listenSocket);
400
0
        return UA_STATUSCODE_BADINTERNALERROR;
401
0
    }
402
600
#endif
403
404
    /* Allow rebinding to the IP/port combination. Eg. to restart the server. */
405
600
    if(reuseaddr &&
406
600
       UA_EventLoopPOSIX_setReusable(listenSocket) != UA_STATUSCODE_GOOD) {
407
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
408
0
                       "TCP %u\t| Could not make the socket addr reusable",
409
0
                       (unsigned)listenSocket);
410
0
        UA_close(listenSocket);
411
0
        return UA_STATUSCODE_BADINTERNALERROR;
412
0
    }
413
414
    /* Set the socket non-blocking */
415
600
    if(UA_EventLoopPOSIX_setNonBlocking(listenSocket) != UA_STATUSCODE_GOOD) {
416
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
417
0
                       "TCP %u\t| Could not set the socket non-blocking",
418
0
                       (unsigned)listenSocket);
419
0
        UA_close(listenSocket);
420
0
        return UA_STATUSCODE_BADINTERNALERROR;
421
0
    }
422
423
    /* Supress interrupts from the socket */
424
600
    if(UA_EventLoopPOSIX_setNoSigPipe(listenSocket) != UA_STATUSCODE_GOOD) {
425
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
426
0
                       "TCP %u\t| Could not disable SIGPIPE",
427
0
                       (unsigned)listenSocket);
428
0
        UA_close(listenSocket);
429
0
        return UA_STATUSCODE_BADINTERNALERROR;
430
0
    }
431
432
    /* Bind socket to address */
433
600
    UA_RESET_ERRNO;
434
600
    int ret = UA_bind(listenSocket, ai->ai_addr, (socklen_t)ai->ai_addrlen);
435
436
    /* Get the port being used if dynamic porting was used */
437
600
    if(port == 0) {
438
0
        struct sockaddr_in sin;
439
0
        memset(&sin, 0, sizeof(sin));
440
0
        socklen_t len = sizeof(sin);
441
0
        UA_getsockname(listenSocket, (struct sockaddr *)&sin, &len);
442
0
        port = ntohs(sin.sin_port);
443
0
    }
444
445
    /* If the INADDR_ANY is used, use the local hostname */
446
600
    char hoststr[UA_MAXHOSTNAME_LENGTH];
447
600
    if(hostname) {
448
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
449
0
                    "TCP %u\t| Creating listen socket for \"%s\" on port %u",
450
0
                    (unsigned)listenSocket, hostname, port);
451
600
    } else {
452
600
        UA_gethostname(hoststr, UA_MAXHOSTNAME_LENGTH);
453
600
        hostname = hoststr;
454
600
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
455
600
                    "TCP %u\t| Creating listen socket for \"%s\" "
456
600
                    "(with local hostname \"%s\") on port %u",
457
600
                    (unsigned)listenSocket, addrstr, hostname, port);
458
600
    }
459
460
600
    if(ret < 0) {
461
0
        UA_LOG_SOCKET_ERRNO_WRAP(
462
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
463
0
                          "TCP %u\t| Error binding the socket to the address %s (%s)",
464
0
                          (unsigned)listenSocket, hostname, errno_str));
465
0
        UA_close(listenSocket);
466
0
        return UA_STATUSCODE_BADINTERNALERROR;
467
0
    }
468
469
    /* Only validate, don't actually start listening */
470
600
    if(validate) {
471
0
        UA_EventLoopPOSIX_setReusable(listenSocket); /* Ensure reuse is possible */
472
0
        UA_close(listenSocket);
473
0
        return UA_STATUSCODE_GOOD;
474
0
    }
475
476
    /* Start listening */
477
600
    UA_RESET_ERRNO;
478
600
    if(UA_listen(listenSocket, UA_MAXBACKLOG) < 0) {
479
0
        UA_LOG_SOCKET_ERRNO_WRAP(
480
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
481
0
                          "TCP %u\t| Error listening on the socket (%s)",
482
0
                          (unsigned)listenSocket, errno_str));
483
0
        UA_EventLoopPOSIX_setReusable(listenSocket); /* Ensure reuse is possible */
484
0
        UA_close(listenSocket);
485
0
        return UA_STATUSCODE_BADINTERNALERROR;
486
0
    }
487
488
    /* Allocate the connection */
489
600
    TCP_FD *newConn = (TCP_FD*)UA_calloc(1, sizeof(TCP_FD));
490
600
    if(!newConn) {
491
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
492
0
                       "TCP %u\t| Error allocating memory for the socket",
493
0
                       (unsigned)listenSocket);
494
0
        UA_EventLoopPOSIX_setReusable(listenSocket); /* Ensure reuse is possible */
495
0
        UA_close(listenSocket);
496
0
        return UA_STATUSCODE_BADINTERNALERROR;
497
0
    }
498
499
600
    newConn->rfd.fd = listenSocket;
500
600
    newConn->rfd.listenEvents = UA_FDEVENT_IN;
501
600
    newConn->rfd.es = &pcm->cm.eventSource;
502
600
    newConn->rfd.eventSourceCB = (UA_FDCallback)TCP_listenSocketCallback;
503
600
    newConn->applicationCB = connectionCallback;
504
600
    newConn->application = application;
505
600
    newConn->context = context;
506
507
    /* Register in the EventLoop */
508
600
    UA_StatusCode res = UA_EventLoopPOSIX_registerFD(el, &newConn->rfd);
509
600
    if(res != UA_STATUSCODE_GOOD) {
510
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
511
0
                       "TCP %u\t| Error registering the socket",
512
0
                       (unsigned)listenSocket);
513
0
        UA_free(newConn);
514
0
        UA_EventLoopPOSIX_setReusable(listenSocket); /* Ensure reuse is possible */
515
0
        UA_close(listenSocket);
516
0
        return res;
517
0
    }
518
519
    /* Register internally */
520
600
    ZIP_INSERT(UA_FDTree, &pcm->fds, &newConn->rfd);
521
600
    pcm->fdsSize++;
522
523
    /* Set up the callback parameters */
524
600
    UA_String listenAddress = UA_STRING((char*)(uintptr_t)hostname);
525
600
    UA_KeyValuePair params[2];
526
600
    params[0].key = UA_QUALIFIEDNAME(0, "listen-address");
527
600
    UA_Variant_setScalar(&params[0].value, &listenAddress, &UA_TYPES[UA_TYPES_STRING]);
528
600
    params[1].key = UA_QUALIFIEDNAME(0, "listen-port");
529
600
    UA_Variant_setScalar(&params[1].value, &port, &UA_TYPES[UA_TYPES_UINT16]);
530
600
    UA_KeyValueMap paramMap = {2, params};
531
532
    /* Announce the listen-socket in the application */
533
600
    connectionCallback(&pcm->cm, (uintptr_t)listenSocket,
534
600
                       application, &newConn->context,
535
600
                       UA_CONNECTIONSTATE_ESTABLISHED,
536
600
                       &paramMap, UA_BYTESTRING_NULL);
537
538
600
    return UA_STATUSCODE_GOOD;
539
600
}
540
541
static UA_StatusCode
542
TCP_registerListenSockets(UA_POSIXConnectionManager *pcm, const char *hostname,
543
                          UA_UInt16 port, void *application, void *context,
544
                          UA_ConnectionManager_connectionCallback connectionCallback,
545
300
                          UA_Boolean validate, UA_Boolean reuseaddr) {
546
300
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
547
548
    /* Create a string for the port */
549
300
    char portstr[6];
550
300
    mp_snprintf(portstr, sizeof(portstr), "%d", port);
551
552
    /* Get all the interface and IPv4/6 combinations for the configured hostname */
553
300
    struct addrinfo hints, *res;
554
300
    memset(&hints, 0, sizeof hints);
555
300
#if UA_IPV6
556
300
    hints.ai_family = AF_UNSPEC; /* Allow IPv4 and IPv6 */
557
#else
558
    hints.ai_family = AF_INET;   /* IPv4 only */
559
#endif
560
300
    hints.ai_socktype = SOCK_STREAM;
561
300
    hints.ai_protocol = IPPROTO_TCP;
562
300
    hints.ai_flags = AI_PASSIVE;
563
564
300
    UA_RESET_ERRNO;
565
300
    int retcode = UA_getaddrinfo(hostname, portstr, &hints, &res);
566
300
    if(retcode != 0) {
567
0
       UA_LOG_SOCKET_ERRNO_GAI_WRAP(
568
0
       UA_LOG_WARNING(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
569
0
                      "TCP\t| Lookup for \"%s\" on port %u failed (%s)",
570
0
                      hostname, port, errno_str));
571
0
        return UA_STATUSCODE_BADINTERNALERROR;
572
0
    }
573
574
    /* Add listen sockets. Aggregate the results to see if at least one
575
     * listen-socket was established. */
576
300
    UA_StatusCode total_result = UA_INT32_MAX;
577
300
    struct addrinfo *ai = res;
578
900
    while(ai) {
579
600
        total_result &= TCP_registerListenSocket(pcm, ai, hostname, port, application, context,
580
600
                                                 connectionCallback, validate, reuseaddr);
581
600
        ai = ai->ai_next;
582
600
    }
583
300
    UA_freeaddrinfo(res);
584
585
300
    return total_result;
586
300
}
587
588
/* Close the connection via a delayed callback */
589
static void
590
677
TCP_shutdown(UA_ConnectionManager *cm, TCP_FD *conn) {
591
    /* Already closing - nothing to do */
592
677
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
593
677
    UA_LOCK_ASSERT(&el->elMutex);
594
595
677
    if(conn->rfd.dc.callback) {
596
0
        UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
597
0
                     "TCP %u\t| Cannot close - already closing",
598
0
                     (unsigned)conn->rfd.fd);
599
0
        return;
600
0
    }
601
602
    /* Shutdown the socket to cancel the current select/epoll */
603
677
    UA_shutdown(conn->rfd.fd, UA_SHUT_RDWR);
604
605
677
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
606
677
                 "TCP %u\t| Shutdown triggered",
607
677
                 (unsigned)conn->rfd.fd);
608
609
    /* Add to the delayed callback list. Will be cleaned up in the next
610
     * iteration. */
611
677
    UA_DelayedCallback *dc = &conn->rfd.dc;
612
677
    dc->callback = TCP_delayedClose;
613
677
    dc->application = cm;
614
677
    dc->context = conn;
615
616
    /* Adding a delayed callback does not take a lock */
617
677
    UA_EventLoopPOSIX_addDelayedCallback((UA_EventLoop*)el, dc);
618
677
}
619
620
static UA_StatusCode
621
677
TCP_shutdownConnection(UA_ConnectionManager *cm, uintptr_t connectionId) {
622
677
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
623
677
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)cm->eventSource.eventLoop;
624
677
    UA_LOCK(&el->elMutex);
625
626
677
    UA_FD fd = (UA_FD)connectionId;
627
677
    TCP_FD *conn = (TCP_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
628
677
    if(!conn) {
629
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
630
0
                       "TCP\t| Cannot close TCP connection %u - not found",
631
0
                       (unsigned)connectionId);
632
0
        UA_UNLOCK(&el->elMutex);
633
0
        return UA_STATUSCODE_BADNOTFOUND;
634
0
    }
635
636
677
    TCP_shutdown(cm, conn);
637
638
677
    UA_UNLOCK(&el->elMutex);
639
677
    return UA_STATUSCODE_GOOD;
640
677
}
641
642
static UA_StatusCode
643
TCP_sendWithConnection(UA_ConnectionManager *cm, uintptr_t connectionId,
644
58
                       const UA_KeyValueMap *params, UA_ByteString *buf) {
645
    /* We may not have a lock. But we need not take it. As the connectionId is
646
     * the fd, no need to do a lookup and access internal data strucures. */
647
648
    /* Prevent OS signals when sending to a closed socket */
649
58
    int flags = MSG_NOSIGNAL;
650
651
58
    struct pollfd tmp_poll_fd;
652
58
    tmp_poll_fd.fd = (UA_FD)connectionId;
653
58
    tmp_poll_fd.events = UA_POLLOUT;
654
655
    /* Send the full buffer. This may require several calls to send */
656
58
    size_t nWritten = 0;
657
58
    do {
658
58
        ssize_t n = 0;
659
58
        do {
660
58
            UA_RESET_ERRNO;
661
58
            UA_LOG_DEBUG(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
662
58
                         "TCP %u\t| Attempting to send", (unsigned)connectionId);
663
58
            size_t bytes_to_send = buf->length - nWritten;
664
58
            n = UA_send((UA_FD)connectionId,
665
58
                        (const char*)buf->data + nWritten,
666
58
                        bytes_to_send, flags);
667
58
            if(n < 0) {
668
                /* An error we cannot recover from? */
669
0
                if(UA_ERRNO != UA_INTERRUPTED && UA_ERRNO != UA_WOULDBLOCK &&
670
0
                   UA_ERRNO != UA_AGAIN)
671
0
                    goto shutdown;
672
673
                /* Poll for the socket resources to become available and retry
674
                 * (blocking) */
675
0
                int poll_ret;
676
0
                do {
677
0
                    UA_RESET_ERRNO;
678
0
                    poll_ret = UA_poll(&tmp_poll_fd, 1, 100);
679
0
                    if(poll_ret < 0 && UA_ERRNO != UA_INTERRUPTED)
680
0
                        goto shutdown;
681
0
                } while(poll_ret <= 0);
682
0
            }
683
58
        } while(n < 0);
684
58
        nWritten += (size_t)n;
685
58
    } while(nWritten < buf->length);
686
687
    /* Clean up and return */
688
58
    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
689
58
    return UA_STATUSCODE_GOOD;
690
691
0
 shutdown:
692
    /* Error -> shutdown the connection  */
693
0
    UA_LOG_SOCKET_ERRNO_WRAP(
694
0
       UA_LOG_ERROR(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
695
0
                    "TCP %u\t| Send failed with error %s",
696
0
                    (unsigned)connectionId, errno_str));
697
0
    TCP_shutdownConnection(cm, connectionId);
698
0
    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
699
0
    return UA_STATUSCODE_BADCONNECTIONCLOSED;
700
58
}
701
702
/* Create a listen-socket that waits for incoming connections */
703
static UA_StatusCode
704
TCP_openPassiveConnection(UA_POSIXConnectionManager *pcm, const UA_KeyValueMap *params,
705
                          void *application, void *context,
706
                          UA_ConnectionManager_connectionCallback connectionCallback,
707
300
                          UA_Boolean validate) {
708
300
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
709
300
    UA_LOCK_ASSERT(&el->elMutex);
710
711
    /* Get the port parameter */
712
300
    const UA_UInt16 *port = (const UA_UInt16*)
713
300
        UA_KeyValueMap_getScalar(params, tcpConnectionParams[TCP_PARAMINDEX_PORT].name,
714
300
                                 &UA_TYPES[UA_TYPES_UINT16]);
715
300
    UA_assert(port); /* existence is checked before */
716
717
    /* Get the address parameter */
718
300
    const UA_Variant *addrs =
719
300
        UA_KeyValueMap_get(params, tcpConnectionParams[TCP_PARAMINDEX_ADDR].name);
720
300
    size_t addrsSize = 0;
721
300
    if(addrs) {
722
0
        UA_assert(addrs->type == &UA_TYPES[UA_TYPES_STRING]);
723
0
        if(UA_Variant_isScalar(addrs))
724
0
            addrsSize = 1;
725
0
        else
726
0
            addrsSize = addrs->arrayLength;
727
0
    }
728
729
    /* Get the reuseaddr parameter */
730
300
    UA_Boolean reuseaddr = false;
731
300
    const UA_Boolean *reuseaddrTmp = (const UA_Boolean*)
732
300
        UA_KeyValueMap_getScalar(params, tcpConnectionParams[TCP_PARAMINDEX_REUSE].name,
733
300
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
734
300
    if(reuseaddrTmp)
735
300
        reuseaddr = *reuseaddrTmp;
736
737
    /* Undefined or empty addresses array -> listen on all interfaces */
738
300
    if(addrsSize == 0) {
739
300
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
740
300
                    "TCP\t| Listening on all interfaces");
741
300
        return TCP_registerListenSockets(pcm, NULL, *port, application,
742
300
                                         context, connectionCallback, validate, reuseaddr);
743
300
    }
744
745
    /* Iterate over the configured hostnames */
746
0
    UA_String *hostStrings = (UA_String*)addrs->data;
747
0
    UA_StatusCode retval = UA_STATUSCODE_BADINTERNALERROR;
748
0
    for(size_t i = 0; i < addrsSize; i++) {
749
0
        char hostname[512];
750
0
        if(hostStrings[i].length >= sizeof(hostname))
751
0
            continue;
752
0
        memcpy(hostname, hostStrings[i].data, hostStrings->length);
753
0
        hostname[hostStrings->length] = '\0';
754
0
        if(TCP_registerListenSockets(pcm, hostname, *port, application,
755
0
                                     context, connectionCallback, validate, reuseaddr) == UA_STATUSCODE_GOOD)
756
0
            retval = UA_STATUSCODE_GOOD;
757
0
    }
758
0
    return retval;
759
300
}
760
761
/* Open a TCP connection to a remote host */
762
static UA_StatusCode
763
TCP_openActiveConnection(UA_POSIXConnectionManager *pcm, const UA_KeyValueMap *params,
764
                         void *application, void *context,
765
                         UA_ConnectionManager_connectionCallback connectionCallback,
766
0
                         UA_Boolean validate) {
767
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
768
0
    UA_LOCK_ASSERT(&el->elMutex);
769
770
    /* Get the connection parameters */
771
0
    char hostname[UA_MAXHOSTNAME_LENGTH];
772
0
    char portStr[UA_MAXPORTSTR_LENGTH];
773
774
    /* Prepare the port parameter as a string */
775
0
    const UA_UInt16 *port = (const UA_UInt16*)
776
0
        UA_KeyValueMap_getScalar(params, tcpConnectionParams[TCP_PARAMINDEX_PORT].name,
777
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
778
0
    UA_assert(port); /* existence is checked before */
779
0
    mp_snprintf(portStr, UA_MAXPORTSTR_LENGTH, "%d", *port);
780
781
    /* Prepare the hostname string */
782
0
    const UA_String *addr = (const UA_String*)
783
0
        UA_KeyValueMap_getScalar(params, tcpConnectionParams[TCP_PARAMINDEX_ADDR].name,
784
0
                                 &UA_TYPES[UA_TYPES_STRING]);
785
0
    if(!addr) {
786
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
787
0
                     "TCP\t| Open TCP Connection: No hostname defined, aborting");
788
0
        return UA_STATUSCODE_BADINTERNALERROR;
789
0
    }
790
0
    if(addr->length >= UA_MAXHOSTNAME_LENGTH) {
791
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
792
0
                     "TCP\t| Open TCP Connection: Hostname too long, aborting");
793
0
        return UA_STATUSCODE_BADINTERNALERROR;
794
0
    }
795
0
    strncpy(hostname, (const char*)addr->data, addr->length);
796
0
    hostname[addr->length] = 0;
797
798
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
799
0
                 "TCP\t| Open a connection to \"%s\" on port %s", hostname, portStr);
800
801
    /* Create the socket description from the connectString
802
     * TODO: Make this non-blocking */
803
0
    UA_RESET_ERRNO;
804
0
    struct addrinfo hints, *info;
805
0
    memset(&hints, 0, sizeof(struct addrinfo));
806
0
    hints.ai_family = AF_UNSPEC;
807
0
    hints.ai_socktype = SOCK_STREAM;
808
0
    int error = UA_getaddrinfo(hostname, portStr, &hints, &info);
809
0
    if(error != 0) {
810
0
       UA_LOG_SOCKET_ERRNO_GAI_WRAP(
811
0
       UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
812
0
                      "TCP\t| Lookup of %s failed (%s)",
813
0
                      hostname, errno_str));
814
0
        return UA_STATUSCODE_BADINTERNALERROR;
815
0
    }
816
817
    /* Create a socket */
818
0
    UA_RESET_ERRNO;
819
0
    UA_FD newSock = UA_socket(info->ai_family, info->ai_socktype, info->ai_protocol);
820
0
    if(newSock == UA_INVALID_FD) {
821
0
        UA_freeaddrinfo(info);
822
0
        UA_LOG_SOCKET_ERRNO_WRAP(
823
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
824
0
                           "TCP\t| Could not create socket to connect to %s (%s)",
825
0
                           hostname, errno_str));
826
0
        return UA_STATUSCODE_BADDISCONNECT;
827
0
    }
828
829
    /* Set the socket options */
830
0
    UA_RESET_ERRNO;
831
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
832
0
    res |= UA_EventLoopPOSIX_setNonBlocking(newSock);
833
0
    res |= UA_EventLoopPOSIX_setNoSigPipe(newSock);
834
0
    res |= TCP_setNoNagle(newSock);
835
0
    if(res != UA_STATUSCODE_GOOD) {
836
0
        UA_LOG_SOCKET_ERRNO_WRAP(
837
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
838
0
                           "TCP\t| Could not set socket options: %s", errno_str));
839
0
        UA_freeaddrinfo(info);
840
0
        UA_close(newSock);
841
0
        return res;
842
0
    }
843
844
    /* Only validate, don't actually open the connection */
845
0
    if(validate) {
846
0
        UA_freeaddrinfo(info);
847
0
        UA_close(newSock);
848
0
        return UA_STATUSCODE_GOOD;
849
0
    }
850
851
    /* Non-blocking connect */
852
0
    UA_RESET_ERRNO;
853
0
    error = UA_connect(newSock, info->ai_addr, info->ai_addrlen);
854
0
    UA_freeaddrinfo(info);
855
0
    if(error != 0 &&
856
0
       UA_ERRNO != UA_INPROGRESS &&
857
0
       UA_ERRNO != UA_WOULDBLOCK) {
858
0
        UA_LOG_SOCKET_ERRNO_WRAP(
859
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
860
0
                           "TCP\t| Connecting the socket to %s failed (%s)",
861
0
                           hostname, errno_str));
862
0
        UA_close(newSock);
863
0
        return UA_STATUSCODE_BADDISCONNECT;
864
0
    }
865
866
    /* Allocate the UA_RegisteredFD */
867
0
    TCP_FD *newConn = (TCP_FD*)UA_calloc(1, sizeof(TCP_FD));
868
0
    if(!newConn) {
869
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
870
0
                       "TCP %u\t| Error allocating memory for the socket",
871
0
                       (unsigned)newSock);
872
0
        UA_close(newSock);
873
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
874
0
    }
875
876
0
    newConn->rfd.fd = newSock;
877
0
    newConn->rfd.es = &pcm->cm.eventSource;
878
0
    newConn->rfd.eventSourceCB = (UA_FDCallback)TCP_connectionSocketCallback;
879
0
    newConn->rfd.listenEvents = UA_FDEVENT_OUT; /* Switched to _IN once the
880
                                                 * connection is open */
881
0
    newConn->applicationCB = connectionCallback;
882
0
    newConn->application = application;
883
0
    newConn->context = context;
884
885
    /* Register the fd to trigger when output is possible (the connection is open) */
886
0
    res = UA_EventLoopPOSIX_registerFD(el, &newConn->rfd);
887
0
    if(res != UA_STATUSCODE_GOOD) {
888
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
889
0
                       "TCP\t| Registering the socket to connect to %s failed", hostname);
890
0
        UA_close(newSock);
891
0
        UA_free(newConn);
892
0
        return res;
893
0
    }
894
895
    /* Register internally in the EventSource */
896
0
    ZIP_INSERT(UA_FDTree, &pcm->fds, &newConn->rfd);
897
0
    pcm->fdsSize++;
898
899
0
    UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
900
0
                "TCP %u\t| Opening a connection to \"%s\" on port %s",
901
0
                (unsigned)newSock, hostname, portStr);
902
903
    /* Signal the new connection to the application as asynchonously opening */
904
0
    connectionCallback(&pcm->cm, (uintptr_t)newSock,
905
0
                       application, &newConn->context,
906
0
                       UA_CONNECTIONSTATE_OPENING, &UA_KEYVALUEMAP_NULL,
907
0
                       UA_BYTESTRING_NULL);
908
909
0
    return UA_STATUSCODE_GOOD;
910
0
}
911
912
static UA_StatusCode
913
TCP_openConnection(UA_ConnectionManager *cm, const UA_KeyValueMap *params,
914
                   void *application, void *context,
915
300
                   UA_ConnectionManager_connectionCallback connectionCallback) {
916
300
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
917
300
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
918
300
    UA_LOCK(&el->elMutex);
919
920
300
    if(cm->eventSource.state != UA_EVENTSOURCESTATE_STARTED) {
921
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
922
0
                     "TCP\t| Cannot open a connection for a "
923
0
                     "ConnectionManager that is not started");
924
0
        UA_UNLOCK(&el->elMutex);
925
0
        return UA_STATUSCODE_BADINTERNALERROR;
926
0
    }
927
928
    /* Check the parameters */
929
300
    UA_StatusCode res =
930
300
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "TCP",
931
300
                                        tcpConnectionParams,
932
300
                                        TCP_PARAMETERSSIZE, params);
933
300
    if(res != UA_STATUSCODE_GOOD) {
934
0
        UA_UNLOCK(&el->elMutex);
935
0
        return res;
936
0
    }
937
938
    /* Only validate the parameters? */
939
300
    UA_Boolean validate = false;
940
300
    const UA_Boolean *validateParam = (const UA_Boolean*)
941
300
        UA_KeyValueMap_getScalar(params,
942
300
                                 tcpConnectionParams[TCP_PARAMINDEX_VALIDATE].name,
943
300
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
944
300
    if(validateParam)
945
0
        validate = *validateParam;
946
947
    /* Listen or active connection? */
948
300
    UA_Boolean listen = false;
949
300
    const UA_Boolean *listenParam = (const UA_Boolean*)
950
300
        UA_KeyValueMap_getScalar(params,
951
300
                                 tcpConnectionParams[TCP_PARAMINDEX_LISTEN].name,
952
300
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
953
300
    if(listenParam)
954
300
        listen = *listenParam;
955
956
300
    if(listen) {
957
300
        res = TCP_openPassiveConnection(pcm, params, application, context,
958
300
                                        connectionCallback, validate);
959
300
    } else {
960
0
        res = TCP_openActiveConnection(pcm, params, application, context,
961
0
                                       connectionCallback, validate);
962
0
    }
963
964
300
    UA_UNLOCK(&el->elMutex);
965
300
    return res;
966
300
}
967
968
static UA_StatusCode
969
551
TCP_eventSourceStart(UA_ConnectionManager *cm) {
970
551
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
971
551
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
972
551
    if(!el)
973
0
        return UA_STATUSCODE_BADINTERNALERROR;
974
975
551
    UA_LOCK(&el->elMutex);
976
977
    /* Check the state */
978
551
    if(cm->eventSource.state != UA_EVENTSOURCESTATE_STOPPED) {
979
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
980
0
                     "TCP\t| To start the ConnectionManager, it has to be "
981
0
                     "registered in an EventLoop and not started yet");
982
0
        UA_UNLOCK(&el->elMutex);
983
0
        return UA_STATUSCODE_BADINTERNALERROR;
984
0
    }
985
986
    /* Check the parameters */
987
551
    UA_StatusCode res =
988
551
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "TCP",
989
551
                                        tcpManagerParams, TCP_MANAGERPARAMS,
990
551
                                        &cm->eventSource.params);
991
551
    if(res != UA_STATUSCODE_GOOD)
992
0
        goto finish;
993
994
    /* Allocate the rx buffer */
995
551
    res = UA_EventLoopPOSIX_allocateStaticBuffers(pcm);
996
551
    if(res != UA_STATUSCODE_GOOD)
997
0
        goto finish;
998
999
    /* Set the EventSource to the started state */
1000
551
    cm->eventSource.state = UA_EVENTSOURCESTATE_STARTED;
1001
1002
551
 finish:
1003
551
    UA_UNLOCK(&el->elMutex);
1004
551
    return res;
1005
551
}
1006
1007
static void *
1008
0
TCP_shutdownCB(void *application, UA_RegisteredFD *rfd) {
1009
0
    UA_ConnectionManager *cm = (UA_ConnectionManager*)application;
1010
0
    TCP_shutdown(cm, (TCP_FD*)rfd);
1011
0
    return NULL;
1012
0
}
1013
1014
static void
1015
551
TCP_eventSourceStop(UA_ConnectionManager *cm) {
1016
551
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1017
551
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
1018
551
    (void)el;
1019
1020
551
    UA_LOCK(&el->elMutex);
1021
1022
551
    UA_LOG_DEBUG(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
1023
551
                 "TCP\t| Shutting down the ConnectionManager");
1024
1025
    /* Prevent new connections to open */
1026
551
    cm->eventSource.state = UA_EVENTSOURCESTATE_STOPPING;
1027
1028
    /* Shutdown all existing connection */
1029
551
    ZIP_ITER(UA_FDTree, &pcm->fds, TCP_shutdownCB, cm);
1030
1031
    /* All sockets closed? Otherwise iterate some more. */
1032
551
    TCP_checkStopped(pcm);
1033
1034
551
    UA_UNLOCK(&el->elMutex);
1035
551
}
1036
1037
static UA_StatusCode
1038
551
TCP_eventSourceDelete(UA_ConnectionManager *cm) {
1039
551
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1040
551
    if(cm->eventSource.state >= UA_EVENTSOURCESTATE_STARTING) {
1041
0
        UA_LOG_ERROR(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
1042
0
                     "TCP\t| The EventSource must be stopped before it can be deleted");
1043
0
        return UA_STATUSCODE_BADINTERNALERROR;
1044
0
    }
1045
1046
551
    UA_ByteString_clear(&pcm->rxBuffer);
1047
551
    UA_ByteString_clear(&pcm->txBuffer);
1048
551
    UA_KeyValueMap_clear(&cm->eventSource.params);
1049
551
    UA_String_clear(&cm->eventSource.name);
1050
551
    UA_free(cm);
1051
1052
551
    return UA_STATUSCODE_GOOD;
1053
551
}
1054
1055
static const char *tcpName = "tcp";
1056
1057
UA_ConnectionManager *
1058
551
UA_ConnectionManager_new_POSIX_TCP(const UA_String eventSourceName) {
1059
551
    UA_POSIXConnectionManager *cm = (UA_POSIXConnectionManager*)
1060
551
        UA_calloc(1, sizeof(UA_POSIXConnectionManager));
1061
551
    if(!cm)
1062
0
        return NULL;
1063
1064
551
    cm->cm.eventSource.eventSourceType = UA_EVENTSOURCETYPE_CONNECTIONMANAGER;
1065
551
    UA_String_copy(&eventSourceName, &cm->cm.eventSource.name);
1066
551
    cm->cm.eventSource.start = (UA_StatusCode (*)(UA_EventSource *))TCP_eventSourceStart;
1067
551
    cm->cm.eventSource.stop = (void (*)(UA_EventSource *))TCP_eventSourceStop;
1068
551
    cm->cm.eventSource.free = (UA_StatusCode (*)(UA_EventSource *))TCP_eventSourceDelete;
1069
551
    cm->cm.protocol = UA_STRING((char*)(uintptr_t)tcpName);
1070
551
    cm->cm.openConnection = TCP_openConnection;
1071
551
    cm->cm.allocNetworkBuffer = UA_EventLoopPOSIX_allocNetworkBuffer;
1072
551
    cm->cm.freeNetworkBuffer = UA_EventLoopPOSIX_freeNetworkBuffer;
1073
551
    cm->cm.sendWithConnection = TCP_sendWithConnection;
1074
551
    cm->cm.closeConnection = TCP_shutdownConnection;
1075
551
    return &cm->cm;
1076
551
}
1077
1078
#endif