Coverage Report

Created: 2025-10-13 06:32

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