LCOV - code coverage report
Current view: top level - libs/bsw/cpp2someip/src - TcpProxy.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.1 % 165 114
Test Date: 2026-09-11 12:05:06 Functions: 100.0 % 21 21

            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)
        

Generated by: LCOV version 2.0-1