Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2026 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : #include "someip/TcpProxy.h"
12 :
13 : #include "someip/INetworkListener.h"
14 : #include "someip/NetworkChannel.h"
15 : #include "someip/SomeIpMessage.h"
16 : #include "someip/logger.h"
17 :
18 : #include <ip/to_str.h>
19 :
20 : #include <etl/algorithm.h>
21 : #include <etl/error_handler.h>
22 :
23 : #include <tuple>
24 :
25 : // Logger API uses printf-style varargs for fixed diagnostic messages in this module.
26 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
27 :
28 : namespace someip
29 : {
30 : using ::tcp::AbstractSocket;
31 : using ::util::logger::SOMEIP;
32 :
33 108 : TcpProxy::TcpProxy(::tcp::AbstractSocket& socket)
34 108 : : _socket(socket)
35 108 : , _pConnectionListener(nullptr)
36 108 : , _pParentServerListener(nullptr)
37 108 : , _pBuffer()
38 108 : , _bufferOffset(0U)
39 108 : , _dynamicLocalPort(0U)
40 : {
41 108 : _socket.setDataListener(this);
42 108 : }
43 :
44 136 : bool TcpProxy::isInitialized() const { return NetworkResource::isInitialized(); }
45 :
46 27 : AbstractSocket& TcpProxy::getSocket() const { return _socket; }
47 :
48 59 : void TcpProxy::setConnectionListener(IConnectionListener* const listener)
49 : {
50 59 : _pConnectionListener = listener;
51 59 : }
52 :
53 58 : bool TcpProxy::isOpen() const { return !_socket.isClosed(); }
54 :
55 5 : bool TcpProxy::isConnected() const { return isInitialized() && _socket.isEstablished(); }
56 :
57 23 : bool TcpProxy::isIdle() const
58 : {
59 23 : return ((isInitialized()) && (_pParentServerListener == nullptr) && (!isOpen()));
60 : }
61 :
62 29 : ::etl::expected<uint16_t, PortError> TcpProxy::getLocalPort() const
63 : {
64 29 : if (isInitialized())
65 : {
66 27 : return _socket.getLocalPort();
67 : }
68 :
69 4 : return ::etl::unexpected<PortError>(PortError::NOT_INITIALIZED);
70 : }
71 :
72 1 : uint8_t TcpProxy::getProto() const { return proto::SD_L4_PROTO_TCP; }
73 :
74 11 : ::ip::IPEndpoint TcpProxy::getRemoteEndpoint() const
75 : {
76 11 : if (isInitialized())
77 : {
78 11 : return ::ip::IPEndpoint(_socket.getRemoteIPAddress(), _socket.getRemotePort());
79 : }
80 :
81 0 : return NetworkResource::INVALID_ADDRESS;
82 : }
83 :
84 2 : bool TcpProxy::open(::ip::IPEndpoint const& localEndpoint, ::ip::IPEndpoint const& remoteEndpoint)
85 : {
86 2 : if (_pBuffer.size() == 0U)
87 : {
88 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() no reassembly buffer", this);
89 0 : return false;
90 : }
91 :
92 2 : if (!isInitialized())
93 : {
94 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() not initialized", this);
95 0 : return false;
96 : }
97 :
98 2 : if (isOpen())
99 : {
100 0 : return true;
101 : }
102 :
103 2 : if (!localEndpoint.isSet())
104 : {
105 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() no local endpoint", this);
106 0 : return false;
107 : }
108 :
109 2 : if (!remoteEndpoint.isSet())
110 : {
111 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() no remote endpoint", this);
112 0 : return false;
113 : }
114 :
115 : char localEndpointStr[::ip::MAX_ENDPOINT_STRING_LENGTH];
116 : char remoteEndpointStr[::ip::MAX_ENDPOINT_STRING_LENGTH];
117 2 : INFO_LOG(
118 : SOMEIP,
119 : "TcpProxy[%p]::open() at: %s, to: %s",
120 : this,
121 : ::ip::to_str(localEndpoint, localEndpointStr).data(),
122 : ::ip::to_str(remoteEndpoint, remoteEndpointStr).data());
123 :
124 : // We need to make modifiable copy of localEndpoint
125 2 : ::ip::IPEndpoint modLocalEndpoint(localEndpoint);
126 :
127 : PortRangeReturnCode rc;
128 2 : etl::tie(_dynamicLocalPort, rc)
129 4 : = computeNextLocalPort(modLocalEndpoint.getPort(), _dynamicLocalPort);
130 2 : if (rc == PortRangeReturnCode::ERROR_REQUESTED_PORT_OUT_OF_RANGE)
131 : {
132 0 : ERROR_LOG(SOMEIP, "Requested port is not in a list of available port ranges");
133 0 : return false;
134 : }
135 :
136 2 : if (rc == PortRangeReturnCode::WARNING_INVALID_CURRENT_PORT)
137 : {
138 0 : WARN_LOG(
139 : SOMEIP,
140 : "Port passed to port range function was invalid, but valid port should be returned.");
141 : }
142 :
143 2 : modLocalEndpoint.setPort(_dynamicLocalPort);
144 :
145 : AbstractSocket::ErrorCode result
146 2 : = _socket.bind(modLocalEndpoint.getAddress(), modLocalEndpoint.getPort());
147 :
148 2 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK != result)
149 : {
150 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() bind failed: %d", this, result);
151 0 : return false;
152 : }
153 :
154 2 : _socket.disableNagleAlgorithm();
155 :
156 4 : result = _socket.connect(
157 : remoteEndpoint.getAddress(),
158 2 : remoteEndpoint.getPort(),
159 : AbstractSocket::ConnectedDelegate::create<TcpProxy, &TcpProxy::connected>(*this));
160 :
161 2 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK != result)
162 : {
163 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::open() connect failed: %d", this, result);
164 0 : (void)_socket.close();
165 0 : return false;
166 : }
167 :
168 2 : return true;
169 : }
170 :
171 4 : void TcpProxy::close()
172 : {
173 4 : if ((!isInitialized()) || (!isOpen()))
174 : {
175 0 : return;
176 : }
177 :
178 4 : INFO_LOG(SOMEIP, "TcpProxy[%p]: close", this);
179 :
180 4 : AbstractSocket::ErrorCode const result = _socket.close();
181 4 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK != result)
182 : {
183 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::close() failed: %d", this, result);
184 : }
185 :
186 4 : closeConnection();
187 : }
188 :
189 1 : void TcpProxy::abort()
190 : {
191 1 : if (!isOpen())
192 : {
193 0 : return;
194 : }
195 :
196 1 : INFO_LOG(SOMEIP, "TcpProxy[%p]: abort", this);
197 1 : _socket.abort();
198 1 : closeConnection();
199 : }
200 :
201 1 : bool TcpProxy::send(::ip::IPEndpoint const& remoteEndpoint, uint32_t const length)
202 : {
203 1 : return NetworkResource::send(remoteEndpoint, length);
204 : }
205 :
206 3 : bool TcpProxy::send(uint32_t length)
207 : {
208 3 : if (!isConnected())
209 : {
210 1 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::send() not connected", this);
211 1 : return false;
212 : }
213 :
214 2 : if (length == 0U)
215 : {
216 1 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::send() no data", this);
217 1 : return false;
218 : }
219 :
220 1 : if (length > _pOutputBuffer.size())
221 : {
222 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::send() too much data (%d bytes)", this, length);
223 0 : return false;
224 : }
225 :
226 1 : uint32_t offset = 0U;
227 1 : while (length > 0U)
228 : {
229 1 : uint32_t const chunk = (length > UINT16_MAX) ? static_cast<uint32_t>(UINT16_MAX) : length;
230 :
231 1 : AbstractSocket::ErrorCode const result = _socket.send(
232 1 : ::etl::span<uint8_t const>(&_pOutputBuffer[offset], static_cast<uint16_t>(chunk)));
233 :
234 1 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK != result)
235 : {
236 1 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::send() failed %d", this, result);
237 1 : return false;
238 : }
239 0 : offset += chunk;
240 0 : length -= chunk;
241 : }
242 :
243 0 : AbstractSocket::ErrorCode const flushResult = _socket.flush();
244 0 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK != flushResult)
245 : {
246 0 : ERROR_LOG(SOMEIP, "TcpProxy[%p]::send() flush failed %d", this, flushResult);
247 0 : return false;
248 : }
249 :
250 0 : return true;
251 : }
252 :
253 1 : void TcpProxy::dataReceived(uint16_t length)
254 : {
255 1 : ::ip::IPEndpoint const endpoint(_socket.getRemoteIPAddress(), _socket.getRemotePort());
256 :
257 2 : while (length > 0U)
258 : {
259 1 : uint32_t const freeBytes = static_cast<uint32_t>(_pBuffer.size() - _bufferOffset);
260 :
261 1 : if (freeBytes == 0U)
262 : {
263 0 : ERROR_LOG(
264 : SOMEIP,
265 : "TcpProxy[%p]::received() buffer exhausted (%d bytes)",
266 : this,
267 : _bufferOffset);
268 0 : _bufferOffset = 0U;
269 0 : return;
270 : }
271 :
272 1 : uint32_t const chunk = (length > freeBytes) ? freeBytes : static_cast<uint32_t>(length);
273 :
274 : uint32_t const read = static_cast<uint32_t>(
275 1 : _socket.read(&_pBuffer[_bufferOffset], static_cast<size_t>(chunk)));
276 :
277 1 : if (read != chunk)
278 : {
279 0 : ERROR_LOG(
280 : SOMEIP, "TcpProxy[%p]::received() read failed (%d / %d bytes)", this, read, chunk);
281 0 : _bufferOffset = 0U;
282 0 : return;
283 : }
284 :
285 1 : _bufferOffset += chunk;
286 1 : length -= static_cast<uint16_t>(chunk);
287 :
288 1 : uint32_t validBytes = 0U;
289 : while (true)
290 : {
291 1 : uint32_t const messageLength = parseMessageLength(validBytes);
292 1 : if ((messageLength == 0U) || (messageLength > (_bufferOffset - validBytes)))
293 : {
294 : break;
295 : }
296 :
297 0 : validBytes += messageLength;
298 0 : }
299 :
300 1 : if (validBytes > 0U)
301 : {
302 0 : etl::copy_n(_pBuffer.begin(), validBytes, _pInputBuffer.begin());
303 0 : incRefCounter(); // in case of hanging proxy.
304 : {
305 0 : NetworkChannel channel(*this, endpoint);
306 0 : if (_pListener != nullptr)
307 : {
308 0 : _pListener->received(channel, validBytes);
309 : }
310 0 : }
311 0 : decRefCounter();
312 0 : _bufferOffset -= validBytes;
313 0 : if (_bufferOffset > 0U)
314 : {
315 0 : auto src = _pBuffer.subspan(validBytes, _bufferOffset);
316 0 : etl::copy(src.begin(), src.end(), _pBuffer.begin());
317 : }
318 : }
319 : }
320 : }
321 :
322 1 : uint32_t TcpProxy::parseMessageLength(uint32_t const offset) const
323 : {
324 1 : uint32_t const length = _bufferOffset - offset;
325 1 : if (length < SomeIpMessage::OFFSET_PAYLOAD)
326 : {
327 1 : return 0U;
328 : }
329 :
330 0 : SomeIpMessage const message(_pBuffer.subspan(offset, length));
331 0 : return SomeIpMessage::OFFSET_PAYLOAD + message.getPayloadLength();
332 : }
333 :
334 1 : void TcpProxy::connected(AbstractSocket::ErrorCode const status)
335 : {
336 1 : if (AbstractSocket::ErrorCode::SOCKET_ERR_OK == status)
337 : {
338 1 : _socket.disableNagleAlgorithm();
339 1 : openConnection(_socket.getLocalPort());
340 : }
341 1 : }
342 :
343 1 : void TcpProxy::connectionClosed(IDataListener::ErrorCode const /* status */) { closeConnection(); }
344 :
345 3 : void TcpProxy::openConnection(port::type const localPort)
346 : {
347 3 : ETL_ASSERT(
348 : _pBuffer.size() > 0U, ETL_ERROR_GENERIC("tcp proxy buffer size must be greater than 0"));
349 3 : INFO_LOG(SOMEIP, "TcpProxy[%p]: open connection at port: %d", this, localPort);
350 :
351 3 : _bufferOffset = 0U;
352 :
353 3 : if (nullptr != _pConnectionListener)
354 : {
355 3 : _pConnectionListener->connectionChanged(*this);
356 : }
357 3 : }
358 :
359 6 : void TcpProxy::closeConnection()
360 : {
361 6 : INFO_LOG(SOMEIP, "TcpProxy[%p]: close connection", this);
362 :
363 6 : _bufferOffset = 0U;
364 :
365 6 : if (nullptr != _pParentServerListener)
366 : {
367 1 : _pParentServerListener->connectionChanged(*this);
368 : }
369 :
370 6 : if (nullptr != _pConnectionListener)
371 : {
372 4 : _pConnectionListener->connectionChanged(*this);
373 : }
374 6 : }
375 :
376 : } // namespace someip
377 :
378 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
|