/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(¶ms[0].value, &listenAddress, &UA_TYPES[UA_TYPES_STRING]); |
696 | 560 | params[1].key = UA_QUALIFIEDNAME(0, "listen-port"); |
697 | 560 | UA_Variant_setScalar(¶ms[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 | ¶mMap, 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 | ¶mMap, 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 |