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