Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2025 Project CHIP Authors
4 : * Copyright (c) 2013-2018 Nest Labs, Inc.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file implements the <tt>Inet::TCPEndPoint</tt> class,
22 : * where the CHIP Inet Layer encapsulates methods for interacting
23 : * with TCP transport endpoints (SOCK_DGRAM sockets on Linux and
24 : * BSD-derived systems) or LwIP TCP protocol control blocks, as
25 : * the system is configured accordingly.
26 : *
27 : */
28 :
29 : #include <inet/TCPEndPoint.h>
30 :
31 : #include <inet/InetFaultInjection.h>
32 : #include <inet/arpa-inet-compatibility.h>
33 :
34 : #include <lib/support/CodeUtils.h>
35 : #include <lib/support/SafeInt.h>
36 : #include <lib/support/logging/CHIPLogging.h>
37 : #include <system/SystemFaultInjection.h>
38 :
39 : #include <stdio.h>
40 : #include <string.h>
41 : #include <utility>
42 :
43 : namespace chip {
44 : namespace Inet {
45 :
46 28 : CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr)
47 : {
48 28 : VerifyOrReturnError(mState == State::kReady, CHIP_ERROR_INCORRECT_STATE);
49 27 : CHIP_ERROR res = CHIP_NO_ERROR;
50 :
51 27 : if (addr != IPAddress::Any && addr.Type() != IPAddressType::kAny && addr.Type() != addrType)
52 : {
53 2 : return INET_ERROR_WRONG_ADDRESS_TYPE;
54 : }
55 :
56 25 : res = BindImpl(addrType, addr, port, reuseAddr);
57 :
58 50 : if (res == CHIP_NO_ERROR)
59 : {
60 24 : mState = State::kBound;
61 : }
62 :
63 25 : return res;
64 : }
65 :
66 25 : CHIP_ERROR TCPEndPoint::Listen(uint16_t backlog)
67 : {
68 25 : VerifyOrReturnError(mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
69 24 : CHIP_ERROR res = CHIP_NO_ERROR;
70 :
71 24 : res = ListenImpl(backlog);
72 :
73 48 : if (res == CHIP_NO_ERROR)
74 : {
75 24 : mState = State::kListening;
76 : }
77 :
78 24 : return res;
79 : }
80 :
81 20 : CHIP_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, InterfaceId intfId)
82 : {
83 20 : VerifyOrReturnError(mState == State::kReady || mState == State::kBound, CHIP_ERROR_INCORRECT_STATE);
84 19 : CHIP_ERROR res = CHIP_NO_ERROR;
85 :
86 19 : ReturnErrorOnFailure(ConnectImpl(addr, port, intfId));
87 :
88 19 : StartConnectTimerIfSet();
89 :
90 19 : return res;
91 : }
92 :
93 12 : CHIP_ERROR TCPEndPoint::Send(System::PacketBufferHandle && data, bool push)
94 : {
95 12 : VerifyOrReturnError(mState == State::kConnected || mState == State::kReceiveShutdown, CHIP_ERROR_INCORRECT_STATE);
96 11 : CHIP_ERROR res = CHIP_NO_ERROR;
97 :
98 11 : bool queueWasEmpty = mSendQueue.IsNull();
99 11 : if (queueWasEmpty)
100 : {
101 11 : mSendQueue = std::move(data);
102 : }
103 : else
104 : {
105 0 : mSendQueue->AddToEnd(std::move(data));
106 : }
107 :
108 11 : ReturnErrorOnFailure(SendQueuedImpl(queueWasEmpty));
109 :
110 11 : if (push)
111 : {
112 11 : res = DriveSending();
113 : }
114 :
115 11 : return res;
116 : }
117 :
118 0 : CHIP_ERROR TCPEndPoint::SetReceivedDataForTesting(System::PacketBufferHandle && data)
119 : {
120 0 : VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE);
121 :
122 0 : mRcvQueue = std::move(data);
123 :
124 0 : return CHIP_NO_ERROR;
125 : }
126 :
127 0 : size_t TCPEndPoint::PendingSendLength()
128 : {
129 0 : if (!mSendQueue.IsNull())
130 : {
131 0 : return mSendQueue->TotalLength();
132 : }
133 0 : return 0;
134 : }
135 :
136 1 : size_t TCPEndPoint::PendingReceiveLength()
137 : {
138 1 : if (!mRcvQueue.IsNull())
139 : {
140 0 : return mRcvQueue->TotalLength();
141 : }
142 1 : return 0;
143 : }
144 :
145 0 : void TCPEndPoint::Shutdown()
146 : {
147 0 : VerifyOrReturn(IsConnected());
148 :
149 : // If fully connected, enter the SendShutdown state.
150 0 : if (mState == State::kConnected)
151 : {
152 0 : mState = State::kSendShutdown;
153 0 : TEMPORARY_RETURN_IGNORED DriveSending();
154 : }
155 :
156 : // Otherwise, if the peer has already closed their end of the connection,
157 0 : else if (mState == State::kReceiveShutdown)
158 : {
159 0 : DoClose(CHIP_NO_ERROR, false);
160 : }
161 : }
162 :
163 66 : void TCPEndPoint::Close()
164 : {
165 : // Clear the receive queue.
166 66 : mRcvQueue = nullptr;
167 :
168 : // Suppress closing callbacks, since the application explicitly called Close().
169 66 : OnConnectionClosed = nullptr;
170 66 : OnPeerClose = nullptr;
171 66 : OnConnectComplete = nullptr;
172 :
173 : // Perform a graceful close.
174 66 : DoClose(CHIP_NO_ERROR, true);
175 66 : }
176 :
177 31 : void TCPEndPoint::Abort()
178 : {
179 : // Suppress closing callbacks, since the application explicitly called Abort().
180 31 : OnConnectionClosed = nullptr;
181 31 : OnPeerClose = nullptr;
182 31 : OnConnectComplete = nullptr;
183 :
184 31 : DoClose(CHIP_ERROR_CONNECTION_ABORTED, true);
185 31 : }
186 :
187 64 : void TCPEndPoint::Free()
188 : {
189 : // Ensure no callbacks to the app after this point.
190 64 : OnAcceptError = nullptr;
191 64 : OnConnectComplete = nullptr;
192 64 : OnConnectionReceived = nullptr;
193 64 : OnConnectionClosed = nullptr;
194 64 : OnPeerClose = nullptr;
195 64 : OnDataReceived = nullptr;
196 64 : OnDataSent = nullptr;
197 :
198 : // Ensure the end point is Closed or Closing.
199 64 : Close();
200 :
201 64 : Delete();
202 64 : }
203 :
204 : #if INET_TCP_IDLE_CHECK_INTERVAL > 0
205 0 : void TCPEndPoint::SetIdleTimeout(uint32_t timeoutMS)
206 : {
207 0 : uint32_t newIdleTimeout = (timeoutMS + (INET_TCP_IDLE_CHECK_INTERVAL - 1)) / INET_TCP_IDLE_CHECK_INTERVAL;
208 0 : bool isIdleTimerRunning = IsIdleTimerRunning(GetEndPointManager());
209 :
210 0 : if (newIdleTimeout > UINT16_MAX)
211 : {
212 0 : newIdleTimeout = UINT16_MAX;
213 : }
214 0 : mIdleTimeout = mRemainingIdleTime = static_cast<uint16_t>(newIdleTimeout);
215 :
216 0 : if (!isIdleTimerRunning && mIdleTimeout)
217 : {
218 0 : TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL),
219 0 : HandleIdleTimer, &GetEndPointManager());
220 : }
221 0 : }
222 :
223 : // static
224 0 : void TCPEndPoint::HandleIdleTimer(chip::System::Layer * aSystemLayer, void * aAppState)
225 : {
226 0 : auto & endPointManager = *reinterpret_cast<EndPointManager<TCPEndPoint> *>(aAppState);
227 0 : bool lTimerRequired = IsIdleTimerRunning(endPointManager);
228 :
229 0 : endPointManager.ForEachEndPoint([](const TCPEndPointHandle & lEndPoint) -> Loop {
230 0 : if (!lEndPoint->IsConnected())
231 0 : return Loop::Continue;
232 0 : if (lEndPoint->mIdleTimeout == 0)
233 0 : return Loop::Continue;
234 :
235 0 : if (lEndPoint->mRemainingIdleTime == 0)
236 : {
237 0 : lEndPoint->DoClose(INET_ERROR_IDLE_TIMEOUT, false);
238 : }
239 : else
240 : {
241 0 : --lEndPoint->mRemainingIdleTime;
242 : }
243 :
244 0 : return Loop::Continue;
245 : });
246 :
247 0 : if (lTimerRequired)
248 : {
249 0 : TEMPORARY_RETURN_IGNORED aSystemLayer->StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL),
250 : HandleIdleTimer, &endPointManager);
251 : }
252 0 : }
253 :
254 : // static
255 0 : bool TCPEndPoint::IsIdleTimerRunning(EndPointManager<TCPEndPoint> & endPointManager)
256 : {
257 : // See if there are any TCP connections with the idle timer check in use.
258 0 : return Loop::Break == endPointManager.ForEachEndPoint([](const TCPEndPointHandle & lEndPoint) {
259 0 : return (lEndPoint->mIdleTimeout == 0) ? Loop::Continue : Loop::Break;
260 0 : });
261 : }
262 :
263 : #endif // INET_TCP_IDLE_CHECK_INTERVAL > 0
264 :
265 0 : CHIP_ERROR TCPEndPoint::SetUserTimeout(uint32_t userTimeoutMillis)
266 : {
267 0 : VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE);
268 0 : CHIP_ERROR res = CHIP_NO_ERROR;
269 :
270 : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
271 :
272 : // Store the User timeout configuration if it is being overridden.
273 0 : mUserTimeoutMillis = userTimeoutMillis;
274 :
275 : #else // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
276 :
277 : res = SetUserTimeoutImpl(userTimeoutMillis);
278 :
279 : #endif // !INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
280 :
281 0 : return res;
282 : }
283 :
284 19 : void TCPEndPoint::StartConnectTimerIfSet()
285 : {
286 19 : if (mConnectTimeoutMsecs > 0)
287 : {
288 19 : TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(mConnectTimeoutMsecs),
289 : TCPConnectTimeoutHandler, this);
290 : }
291 19 : }
292 :
293 82 : void TCPEndPoint::StopConnectTimer()
294 : {
295 82 : GetSystemLayer().CancelTimer(TCPConnectTimeoutHandler, this);
296 82 : }
297 :
298 0 : void TCPEndPoint::TCPConnectTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
299 : {
300 0 : TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
301 0 : VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
302 :
303 : // Close Connection as we have timed out and Connect has not returned to stop this timer.
304 0 : tcpEndPoint->DoClose(INET_ERROR_TCP_CONNECT_TIMEOUT, false);
305 0 : }
306 :
307 313 : bool TCPEndPoint::IsConnected(State state)
308 : {
309 313 : return state == State::kConnected || state == State::kSendShutdown || state == State::kReceiveShutdown ||
310 313 : state == State::kClosing;
311 : }
312 :
313 11 : CHIP_ERROR TCPEndPoint::DriveSending()
314 : {
315 11 : CHIP_ERROR err = DriveSendingImpl();
316 :
317 22 : if (err != CHIP_NO_ERROR)
318 : {
319 0 : DoClose(err, false);
320 : }
321 :
322 11 : CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT();
323 :
324 11 : return err;
325 : }
326 :
327 17 : void TCPEndPoint::DriveReceiving(const TCPEndPointHandle & handle)
328 : {
329 : // If there's data in the receive queue and the app is ready to receive it then call the app's callback
330 : // with the entire receive queue.
331 17 : if (!mRcvQueue.IsNull() && mReceiveEnabled && OnDataReceived != nullptr)
332 : {
333 : // Acknowledgement is done after handling the buffers to allow the
334 : // application processing to throttle flow.
335 9 : size_t ackLength = mRcvQueue->TotalLength();
336 9 : CHIP_ERROR err = OnDataReceived(handle, std::move(mRcvQueue));
337 18 : if (err != CHIP_NO_ERROR)
338 : {
339 0 : DoClose(err, false);
340 0 : return;
341 : }
342 9 : TEMPORARY_RETURN_IGNORED AckReceive(ackLength);
343 : }
344 :
345 : // If the connection is closing, and the receive queue is now empty, call DoClose() to complete
346 : // the process of closing the connection.
347 17 : if (mState == State::kClosing && mRcvQueue.IsNull())
348 : {
349 2 : DoClose(CHIP_NO_ERROR, false);
350 : }
351 : }
352 :
353 18 : void TCPEndPoint::HandleConnectComplete(CHIP_ERROR err)
354 : {
355 18 : TCPEndPointHandle handle(this);
356 : // If the connect succeeded enter the Connected state and call the app's callback.
357 36 : if (err == CHIP_NO_ERROR)
358 : {
359 : // Stop the TCP Connect timer in case it is still running.
360 18 : StopConnectTimer();
361 :
362 : // Mark the connection as being active.
363 18 : MarkActive();
364 :
365 18 : mState = State::kConnected;
366 :
367 18 : HandleConnectCompleteImpl();
368 :
369 18 : if (OnConnectComplete != nullptr)
370 : {
371 18 : OnConnectComplete(handle, CHIP_NO_ERROR);
372 : }
373 : }
374 :
375 : // Otherwise, close the connection with an error.
376 : else
377 : {
378 0 : DoClose(err, false);
379 : }
380 18 : }
381 :
382 105 : void TCPEndPoint::DoClose(CHIP_ERROR err, bool suppressCallback)
383 : {
384 105 : State oldState = mState;
385 :
386 : // If in one of the connected states (Connected, LocalShutdown, PeerShutdown or Closing)
387 : // AND this is a graceful close (i.e. not prompted by an error)
388 : // AND there is data waiting to be processed on either the send or receive queues
389 : // ... THEN enter the Closing state, allowing the queued data to drain,
390 : // ... OTHERWISE go straight to the Closed state.
391 139 : if (IsConnected() && err == CHIP_NO_ERROR && (!mSendQueue.IsNull() || !mRcvQueue.IsNull()))
392 : {
393 0 : mState = State::kClosing;
394 : }
395 : else
396 : {
397 105 : mState = State::kClosed;
398 : }
399 :
400 105 : if (oldState != State::kClosed)
401 : {
402 : // Stop the Connect timer in case it is still running.
403 64 : StopConnectTimer();
404 : }
405 :
406 : // If not making a state transition, return immediately.
407 105 : if (mState == oldState)
408 : {
409 41 : return;
410 : }
411 :
412 64 : DoCloseImpl(err, oldState);
413 :
414 : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
415 : // Stop the TCP UserTimeout timer if it is running.
416 64 : StopTCPUserTimeoutTimer();
417 : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
418 :
419 : // If entering the Closed state...
420 64 : if (mState == State::kClosed)
421 : {
422 : // Clear clear the send and receive queues.
423 64 : mSendQueue = nullptr;
424 64 : mRcvQueue = nullptr;
425 :
426 : // Call the appropriate app callback if allowed.
427 64 : if (!suppressCallback)
428 : {
429 8 : if (oldState == State::kConnecting)
430 : {
431 0 : if (OnConnectComplete != nullptr)
432 : {
433 0 : TCPEndPointHandle handle(this);
434 0 : OnConnectComplete(handle, err);
435 0 : }
436 : }
437 8 : else if ((oldState == State::kConnected || oldState == State::kSendShutdown || oldState == State::kReceiveShutdown ||
438 8 : oldState == State::kClosing) &&
439 8 : OnConnectionClosed != nullptr)
440 : {
441 8 : TCPEndPointHandle handle(this);
442 8 : OnConnectionClosed(handle, err);
443 8 : }
444 : }
445 : }
446 : }
447 :
448 : #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
449 :
450 13 : void TCPEndPoint::ScheduleNextTCPUserTimeoutPoll(uint32_t aTimeOut)
451 : {
452 13 : TEMPORARY_RETURN_IGNORED GetSystemLayer().StartTimer(System::Clock::Milliseconds32(aTimeOut), TCPUserTimeoutHandler, this);
453 13 : }
454 :
455 13 : void TCPEndPoint::StartTCPUserTimeoutTimer()
456 : {
457 13 : ScheduleNextTCPUserTimeoutPoll(mUserTimeoutMillis);
458 13 : mUserTimeoutTimerRunning = true;
459 13 : }
460 :
461 85 : void TCPEndPoint::StopTCPUserTimeoutTimer()
462 : {
463 85 : GetSystemLayer().CancelTimer(TCPUserTimeoutHandler, this);
464 85 : mUserTimeoutTimerRunning = false;
465 85 : }
466 :
467 4 : void TCPEndPoint::RestartTCPUserTimeoutTimer()
468 : {
469 4 : StopTCPUserTimeoutTimer();
470 4 : StartTCPUserTimeoutTimer();
471 4 : }
472 :
473 : // static
474 0 : void TCPEndPoint::TCPUserTimeoutHandler(chip::System::Layer * aSystemLayer, void * aAppState)
475 : {
476 0 : TCPEndPoint * tcpEndPoint = reinterpret_cast<TCPEndPoint *>(aAppState);
477 0 : VerifyOrDie((aSystemLayer != nullptr) && (tcpEndPoint != nullptr));
478 0 : tcpEndPoint->TCPUserTimeoutHandler();
479 0 : }
480 :
481 : #endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
482 :
483 : #if INET_CONFIG_TEST
484 : bool TCPEndPoint::sForceEarlyFailureIncomingConnection = false;
485 : #endif
486 :
487 : } // namespace Inet
488 : } // namespace chip
|