Diag-Client-Lib
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tls_socket.cpp
Go to the documentation of this file.
1 /* Diagnostic Client library
2  * Copyright (C) 2024 Avijit Dey
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  */
8 
10 
11 #include <utility>
12 
14 
15 namespace boost_support {
16 namespace socket {
17 namespace tls {
18 
19 TlsSocket::TlsSocket(std::string_view local_ip_address, std::uint16_t local_port_num,
20  TlsContext &tls_context, IoContext &io_context) noexcept
21  : ssl_stream_{io_context.GetContext(), tls_context.GetContext()},
22  local_endpoint_{boost::asio::ip::make_address(local_ip_address), local_port_num} {}
23 
24 TlsSocket::TlsSocket(TlsSocket::TcpSocket tcp_socket, TlsContext &tls_context) noexcept
25  : ssl_stream_{std::move(tcp_socket), tls_context.GetContext()},
26  local_endpoint_{} {
27  TcpErrorCodeType ec{};
28 
29  // Perform TLS handshake
30  ssl_stream_.handshake(boost::asio::ssl::stream_base::server, ec);
31 
32  if (ec.value() == boost::system::errc::success) {
33  printf("Connected with %s encryption\n", SSL_get_cipher(ssl_stream_.native_handle()));
34  } else {
36  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
37  msg << "Tls client handshake with host failed with error: " << ec.message();
38  });
39  }
40 }
41 
43  : ssl_stream_{std::move(other.ssl_stream_)},
44  local_endpoint_{std::move(other.local_endpoint_)} {}
45 
47  ssl_stream_ = std::move(std::move(other.ssl_stream_));
48  local_endpoint_ = std::move(other.local_endpoint_);
49  return *this;
50 }
51 
52 TlsSocket::~TlsSocket() noexcept = default;
53 
54 core_type::Result<void, TlsSocket::SocketError> TlsSocket::Open() noexcept {
55  core_type::Result<void, SocketError> result{SocketError::kGenericError};
56  TcpErrorCodeType ec{};
57 
58  // Open the socket
59  GetNativeTcpSocket().open(local_endpoint_.protocol(), ec);
60  if (ec.value() == boost::system::errc::success) {
61  // Reuse address
62  GetNativeTcpSocket().set_option(boost::asio::socket_base::reuse_address{true});
63  // Set socket to non blocking
64  GetNativeTcpSocket().non_blocking(false);
65  // Bind to local ip address and random port
66  GetNativeTcpSocket().bind(local_endpoint_, ec);
67 
68  if (ec.value() == boost::system::errc::success) {
69  // Socket binding success
71  FILE_NAME, __LINE__, __func__, [this](std::stringstream &msg) {
72  Tcp::endpoint const endpoint_{GetNativeTcpSocket().local_endpoint()};
73  msg << "Tls Socket opened and bound to "
74  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
75  });
76  result.EmplaceValue();
77  } else {
78  // Socket binding failed
80  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
81  msg << "Tls Socket binding failed with message: " << ec.message();
82  });
83  result.EmplaceError(SocketError::kBindingFailed);
84  }
85  } else {
87  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
88  msg << "Tls Socket opening failed with error: " << ec.message();
89  });
90  result.EmplaceError(SocketError::kOpenFailed);
91  }
92  return result;
93 }
94 
96  std::string_view host_ip_address, std::uint16_t host_port_num) noexcept {
97  core_type::Result<void, SocketError> result{SocketError::kGenericError};
98  TcpErrorCodeType ec{};
99 
100  // Connect to provided Ip address
101  GetNativeTcpSocket().connect(
102  Tcp::endpoint{boost::asio::ip::make_address(host_ip_address), host_port_num}, ec);
103  if (ec.value() == boost::system::errc::success) {
105  FILE_NAME, __LINE__, __func__, [this](std::stringstream &msg) {
106  Tcp::endpoint const endpoint_{GetNativeTcpSocket().remote_endpoint()};
107  msg << "Tls socket connected to host "
108  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
109  });
110  // Perform TLS handshake
111  ssl_stream_.handshake(boost::asio::ssl::stream_base::client, ec);
112  if (ec.value() == boost::system::errc::success) {
113  printf("Connected with %s encryption\n", SSL_get_cipher(ssl_stream_.native_handle()));
114  result.EmplaceValue();
115  } else {
117  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
118  msg << "Tls client handshake with host failed with error: " << ec.message();
119  });
120  result.EmplaceError(SocketError::kTlsHandshakeFailed);
121  }
122  } else {
124  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
125  msg << "Tls client socket connect to host failed with error: " << ec.message();
126  });
127  }
128  return result;
129 }
130 
133  TcpErrorCodeType ec{};
134 
135  // Shutdown TLS connection
136  ssl_stream_.shutdown(ec);
137 
138  // ssl_stream_.
139 
140  if (ec.value() == boost::system::errc::success) {
141  // Socket shutdown success
142  result.EmplaceValue();
143  } else {
145  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
146  msg << "Tls client socket disconnection from host failed with error: " << ec.message();
147  });
148  }
149  return result;
150 }
151 
153  TcpMessageConstPtr tcp_message) noexcept {
154  core_type::Result<void, SocketError> result{SocketError::kGenericError};
155  TcpErrorCodeType ec{};
156 
157  boost::asio::write(
158  ssl_stream_,
159  boost::asio::buffer(tcp_message->GetPayload().data(), tcp_message->GetPayload().size()), ec);
160  // Check for error
161  if (ec.value() == boost::system::errc::success) {
163  FILE_NAME, __LINE__, __func__, [this](std::stringstream &msg) {
164  Tcp::endpoint const endpoint_{GetNativeTcpSocket().remote_endpoint()};
165  msg << "Tcp message sent to "
166  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
167  });
168  result.EmplaceValue();
169  } else {
171  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
172  msg << "Tcp message sending failed with error: " << ec.message();
173  });
174  }
175  return result;
176 }
177 
180  // Shutdown of TCP connection
181  GetNativeTcpSocket().shutdown(Tcp::socket ::shutdown_both);
182 
183  GetNativeTcpSocket().cancel();
184 
185  // Destroy the socket
186  GetNativeTcpSocket().close();
187  result.EmplaceValue();
188  return result;
189 }
190 
193  TcpErrorCodeType ec{};
194  // create and reserve the buffer
195  TcpMessage::BufferType rx_buffer{};
196  rx_buffer.resize(message::tcp::kDoipheadrSize);
197  // start blocking read to read Header first
198  boost::asio::read(ssl_stream_, boost::asio::buffer(&rx_buffer[0u], message::tcp::kDoipheadrSize),
199  ec);
200  // Check for error
201  if (ec.value() == boost::system::errc::success) {
202  // read the next bytes to read
203  std::uint32_t read_next_bytes = [&rx_buffer]() noexcept -> std::uint32_t {
204  return static_cast<std::uint32_t>(
205  (static_cast<std::uint32_t>(rx_buffer[4u] << 24u) & 0xFF000000) |
206  (static_cast<std::uint32_t>(rx_buffer[5u] << 16u) & 0x00FF0000) |
207  (static_cast<std::uint32_t>(rx_buffer[6u] << 8u) & 0x0000FF00) |
208  (static_cast<std::uint32_t>(rx_buffer[7u] & 0x000000FF)));
209  }();
210 
211  if (read_next_bytes != 0u) {
212  Tcp::endpoint const remote_endpoint{GetNativeTcpSocket().remote_endpoint()};
214  FILE_NAME, __LINE__, __func__,
215  [&remote_endpoint, read_next_bytes](std::stringstream &msg) {
216  msg << "Tcp Message with length= " << read_next_bytes << " received from "
217  << "<" << remote_endpoint.address().to_string() << "," << remote_endpoint.port()
218  << ">";
219  });
220  read_next_bytes = 0;
221  // reserve the buffer
222  rx_buffer.resize(message::tcp::kDoipheadrSize + std::size_t{read_next_bytes});
223  boost::asio::read(
224  ssl_stream_,
225  boost::asio::buffer(&rx_buffer[message::tcp::kDoipheadrSize], read_next_bytes), ec);
226 
227  // all message received, transfer to upper layer
228  TcpMessagePtr tcp_rx_message{std::make_unique<TcpMessage>(
229  remote_endpoint.address().to_string(), remote_endpoint.port(), std::move(rx_buffer))};
231  FILE_NAME, __LINE__, __func__,
232  [&remote_endpoint, read_next_bytes](std::stringstream &msg) {
233  msg << "Tcp Message with length= " << read_next_bytes << " received from "
234  << "<" << remote_endpoint.address().to_string() << "," << remote_endpoint.port()
235  << ">";
236  });
237  result.EmplaceValue(std::move(tcp_rx_message));
238  } else {
240  FILE_NAME, __LINE__, __func__,
241  [](std::stringstream &msg) { msg << "Tcp Message read ignored as header size is zero"; });
242  }
243  } else if (ec.value() == boost::asio::error::eof) {
245  FILE_NAME, __LINE__, __func__,
246  [ec](std::stringstream &msg) { msg << "Remote Disconnected with: " << ec.message(); });
247  } else {
249  FILE_NAME, __LINE__, __func__, [ec](std::stringstream &msg) {
250  msg << "Remote Disconnected with undefined error: " << ec.message();
251  });
252  }
253  return result;
254 }
255 
256 TlsSocket::SslStream::lowest_layer_type &TlsSocket::GetNativeTcpSocket() {
257  return ssl_stream_.lowest_layer();
258 }
259 
260 } // namespace tls
261 } // namespace socket
262 } // namespace boost_support
static auto GetLibBoostLogger() noexcept -> LibBoostLogger &
Definition: logger.h:20
std::vector< std::uint8_t > BufferType
Type alias for underlying buffer.
Definition: tcp_message.h:49
Wrapper class to hold boost io context required for io object( sockets)
Definition: io_context.h:22
Tls context class responsible for setting cipher suite and loading certificates.
Definition: tls_context.h:24
Class used to create a tcp socket for handling transmission and reception of tcp message from driver.
Definition: tls_socket.h:25
TlsSocket(std::string_view local_ip_address, std::uint16_t local_port_num, TlsContext &tls_context, IoContext &io_context) noexcept
Constructs an instance of TcpSocket.
Definition: tls_socket.cpp:19
boost::system::error_code TcpErrorCodeType
Type alias for tcp error codes.
Definition: tls_socket.h:156
core_type::Result< TcpMessagePtr, SocketError > Read() noexcept
Function to read message from socket.
Definition: tls_socket.cpp:191
~TlsSocket() noexcept
Destruct an instance of TcpSocket.
boost_support::message::tcp::TcpMessagePtr TcpMessagePtr
Type alias for Tcp message pointer.
Definition: tls_socket.h:46
SslStream::lowest_layer_type & GetNativeTcpSocket()
Function to get the native tcp socket under tls socket.
Definition: tls_socket.cpp:256
core_type::Result< void, SocketError > Close() noexcept
Function to destroy the socket.
Definition: tls_socket.cpp:178
boost_support::message::tcp::TcpMessageConstPtr TcpMessageConstPtr
Type alias for Tcp message const pointer.
Definition: tls_socket.h:51
TlsSocket & operator=(const TlsSocket &other) noexcept=delete
SslStream ssl_stream_
Store the underlying tcp socket.
Definition: tls_socket.h:166
core_type::Result< void, SocketError > Connect(std::string_view host_ip_address, std::uint16_t host_port_num) noexcept
Function to connect to remote ip address and port number.
Definition: tls_socket.cpp:95
Tcp::socket TcpSocket
Type alias for tcp socket.
Definition: tls_socket.h:61
core_type::Result< void, SocketError > Transmit(TcpMessageConstPtr tcp_message) noexcept
Function to trigger transmission.
Definition: tls_socket.cpp:152
core_type::Result< void, SocketError > Disconnect() noexcept
Function to Disconnect from host.
Definition: tls_socket.cpp:131
Class type to contains a value (of type ValueType), or an error (of type ErrorType)
Definition: result.h:29
#define FILE_NAME
Definition: file_path.h:14
constexpr std::uint8_t kDoipheadrSize
Doip HeaderSize.
Definition: tcp_message.h:156
core_type::Result< T, E > Result
Class type to contains a value (of type ValueType), or an error (of type ErrorType)