Coverage Report

Created: 2026-08-31 07:11

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