GCC Code Coverage Report


Directory: ./
File: IpSocket.hpp
Date: 2026-10-14 22:12:07
Exec Total Coverage
Lines: 0 1 0.0%
Functions: 0 1 0.0%
Branches: 0 0 -%

Line Branch Exec Source
1 // ======================================================================
2 // \title IpSocket.hpp
3 // \author mstarch
4 // \brief hpp file for IpSocket core implementation classes
5 //
6 // \copyright
7 // Copyright 2009-2020, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12 #ifndef DRV_IP_IPHELPER_HPP_
13 #define DRV_IP_IPHELPER_HPP_
14
15 #include <Fw/FPrimeBasicTypes.hpp>
16 #include <Os/Mutex.hpp>
17 #include <config/IpCfg.hpp>
18
19 // MSG_NOSIGNAL must be visible when SEND_NO_SIGNAL_FLAGS is defined below, independent of what IpCfg.hpp includes
20 #ifdef TGT_OS_TYPE_VXWORKS
21 #include <sockLib.h>
22 #include <socket.h>
23 #elif defined(TGT_OS_TYPE_LINUX) || defined(TGT_OS_TYPE_DARWIN)
24 #include <sys/socket.h>
25 #endif
26
27 namespace Drv {
28
29 struct SocketDescriptor final {
30 int fd = -1; //!< Used for all sockets to track the communication file descriptor
31 int serverFd = -1; //!< Used for server sockets to track the listening file descriptor
32 };
33
34 /**
35 * \brief Status enumeration for socket return values
36 */
37 enum SocketIpStatus {
38 SOCK_SUCCESS = 0, //!< Socket operation successful
39 SOCK_FAILED_TO_GET_SOCKET = -1, //!< Socket open failed
40 SOCK_FAILED_TO_GET_HOST_IP = -2, //!< Host IP lookup failed
41 SOCK_INVALID_IP_ADDRESS = -3, //!< Bad IP address supplied
42 SOCK_FAILED_TO_CONNECT = -4, //!< Failed to connect socket
43 SOCK_FAILED_TO_SET_SOCKET_OPTIONS = -5, //!< Failed to configure socket
44 SOCK_INTERRUPTED_TRY_AGAIN = -6, //!< Interrupted status for retries
45 SOCK_READ_ERROR = -7, //!< Failed to read socket
46 SOCK_DISCONNECTED = -8, //!< Socket disconnected during read or send
47 SOCK_FAILED_TO_BIND = -9, //!< Failed to bind to socket
48 SOCK_FAILED_TO_LISTEN = -10, //!< Failed to listen on socket
49 SOCK_FAILED_TO_ACCEPT = -11, //!< Failed to accept connection
50 SOCK_SEND_ERROR = -13, //!< Failed to send after configured retries
51 SOCK_NOT_STARTED = -14, //!< Socket has not been started
52 SOCK_FAILED_TO_READ_BACK_PORT = -15, //!< Failed to read back port from connection
53 SOCK_NO_DATA_AVAILABLE = -16, //!< No data available or read operation would block
54 SOCK_ANOTHER_THREAD_OPENING = -17, //!< Another thread is opening
55 SOCK_AUTO_CONNECT_DISABLED = -18, //!< Automatic connections are disabled
56 SOCK_INVALID_CALL = -19 //!< Operation is invalid
57 };
58
59 /**
60 * \brief Helper base-class for setting up Berkeley sockets
61 *
62 * Certain IP headers have conflicting definitions with the m_data member of various types in fprime. TcpHelper
63 * separates the ip setup from the incoming Fw::Buffer in the primary component class preventing this collision.
64 */
65 class IpSocket {
66 public:
67 IpSocket();
68 ✗ virtual ~IpSocket() {};
69 /**
70 * \brief configure the ip socket with an IPv4 address and transmission timeouts
71 *
72 * Configures the IP handler (Tcp, Tcp server, and Udp) to use the given IPv4 address and port.
73 * When multiple ports are used for send/receive these settings affect the send direction (as is
74 * the case for udp).
75 *
76 * \warning DNS resolution is NOT performed by this driver. The \a ipv4_address argument MUST be
77 * a NUL-terminated IPv4 address in dotted-quad notation of the form "x.x.x.x" (for example,
78 * "127.0.0.1"). Passing a textual hostname (e.g. "localhost") will be rejected at open() time
79 * with SOCK_INVALID_IP_ADDRESS. Callers that need DNS lookup must perform it themselves and
80 * pass the resolved IPv4 string to this function.
81 *
82 * Port cannot be set to 0 as dynamic port assignment is not supported.
83 *
84 * Note: for UDP sockets this is equivalent to `configureSend` and only sets up the
85 * transmission direction of the socket. A separate call to `configureRecv` is required to
86 * receive on the socket and should be made before the `open` call has been made.
87 *
88 * \param ipv4_address: IPv4 address (dotted-quad "x.x.x.x") used for outgoing transmissions
89 * (and incoming when tcp).
90 * \param port: port socket uses for outgoing transmissions (and incoming when tcp). Must NOT be 0.
91 * \param send_timeout_seconds: send timeout seconds portion
92 * \param send_timeout_microseconds: send timeout microseconds portion. Must be less than 1000000
93 * \return status of configure
94 */
95 virtual SocketIpStatus configure(const char* const ipv4_address,
96 const U16 port,
97 const U32 send_timeout_seconds,
98 const U32 send_timeout_microseconds);
99
100 /**
101 * \brief open the IP socket for communications
102 *
103 * This will open the IP socket for communication. This method error checks and validates properties set using the
104 * `configure` method. Tcp sockets will open bidirectional communication assuming the `configure` function was
105 * previously called. Udp sockets allow `configureRecv` and `configure`/`configureSend` calls to configure for
106 * each direction separately and may be operated in a single-direction or bidirectional mode. This call returns a
107 * status of SOCK_SEND means the port is ready for transmissions and any other status should be treated as an error
108 * with the socket not capable of sending nor receiving. This method will properly close resources on any
109 * unsuccessful status.
110 *
111 * In the case of server components (TcpServer) this function will block until a client has connected.
112 *
113 * Note: delegates to openProtocol for protocol specific implementation
114 *
115 * \param socketDescriptor: socket descriptor to update with opened port
116 * \return status of open
117 */
118 SocketIpStatus open(SocketDescriptor& socketDescriptor);
119 /**
120 * \brief send data out the IP socket from the given buffer
121 *
122 * Sends data out of the IpSocket. This outgoing transmission will be retried several times if the transmission
123 * fails to send all the data. Retries are globally configured in the `IpCfg.hpp` header. Should the
124 * socket be unavailable, SOCK_DISCONNECTED is returned and the socket should be reopened using the `open` call.
125 * This can happen even when the socket has already been opened should a transmission error/closure be detected.
126 * Unless an error is received, all data will have been transmitted. A zero-size send is a no-op returning
127 * SOCK_SUCCESS. Should the send be interrupted or time out (SO_SNDTIMEO) before all data is transmitted,
128 * SOCK_INTERRUPTED_TRY_AGAIN is returned and the caller should retry the send.
129 *
130 * Note: delegates to `sendProtocol` to send the data
131 *
132 * \param fd: file descriptor to send to
133 * \param data: pointer to data to send
134 * \param size: size of data to send
135 * \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success,
136 * SOCK_INTERRUPTED_TRY_AGAIN to retry, something else on error
137 */
138 virtual SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size);
139 /**
140 * \brief receive data from the IP socket from the given buffer
141 *
142 * Receives data from the IpSocket. Should the socket be unavailable, SOCK_DISCONNECTED will be returned and
143 * the socket should be reopened using the `open` call. This can happen even when the socket has already been opened
144 * should a transmission error/closure be detected. Since this blocks until data is available, it will retry as long
145 * as EINTR is set and less than a max number of iterations has passed. This function will block to receive data and
146 * will retry (up to a configured set of retries) as long as EINTR is returned.
147 *
148 * Note: delegates to `recvProtocol` to send the data
149 *
150 * \param socketDescriptor: socket descriptor to recv from
151 * \param data: pointer to data to fill with received data
152 * \param size: maximum size of data buffer to fill
153 * \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
154 */
155 SocketIpStatus recv(const SocketDescriptor& fd, U8* const data, FwSizeType& size);
156
157 /**
158 * \brief closes the socket
159 *
160 * Closes the socket opened by the open call. In the case of the TcpServer, this does NOT close server's listening
161 * port but will close the active client connection.
162 *
163 * \param socketDescriptor: socket descriptor to close
164 */
165 void close(const SocketDescriptor& socketDescriptor);
166
167 /**
168 * \brief shutdown the socket
169 *
170 * Shuts down the socket opened by the open call. In the case of the TcpServer, this does not shut down server's
171 * listening port, but rather shuts down the active client.
172 *
173 * A shut down begins the termination of communication. The underlying socket will coordinate a clean shutdown, and
174 * it is safe to close the socket once a recv with 0 size has returned or an appropriate timeout has been reached.
175 *
176 * \param socketDescriptor: socket descriptor to shutdown
177 */
178 void shutdown(const SocketDescriptor& socketDescriptor);
179
180 protected:
181 /**
182 * \brief Check if the given port is valid for the socket
183 *
184 * Some ports should be allowed for sockets and disabled on others (e.g. port 0 is a valid tcp server port but not a
185 * client. This will check the port and return "true" if the port is valid, or "false" otherwise. In the default
186 * implementation, all ports are considered valid.
187 *
188 * \param port: port to check
189 * \return true if valid, false otherwise
190 */
191 virtual bool isValidPort(U16 port) const;
192
193 /**
194 * \brief setup the socket timeout properties of the opened outgoing socket
195 * \param socketDescriptor: socket descriptor to setup
196 * \return status of timeout setup
197 */
198 SocketIpStatus setupTimeouts(int socketFd);
199
200 //! Flags added to every TCP send, on top of SOCKET_IP_SEND_FLAGS. MSG_NOSIGNAL makes a send to a peer that has
201 //! closed the connection fail with EPIPE instead of raising SIGPIPE, which would terminate the process. Where
202 //! MSG_NOSIGNAL is not defined, setupNoSigPipe covers platforms that provide SO_NOSIGPIPE (e.g. macOS).
203 #ifdef MSG_NOSIGNAL
204 static constexpr int SEND_NO_SIGNAL_FLAGS = MSG_NOSIGNAL;
205 #else
206 static constexpr int SEND_NO_SIGNAL_FLAGS = 0;
207 #endif
208
209 /**
210 * \brief prevent sends on a stream socket from raising SIGPIPE on platforms without MSG_NOSIGNAL
211 *
212 * Sets SO_NOSIGPIPE where the platform provides it (e.g. macOS). Elsewhere this does nothing: sends pass
213 * SEND_NO_SIGNAL_FLAGS instead.
214 *
215 * \param socketFd: stream socket to setup; may be called before connect
216 * \return SOCK_SUCCESS, or SOCK_FAILED_TO_SET_SOCKET_OPTIONS if the option could not be set
217 */
218 SocketIpStatus setupNoSigPipe(int socketFd);
219
220 /**
221 * \brief converts a given IPv4 address in dotted-quad form "x.x.x.x" to a network-order
222 * in_addr structure. ONLY works for IPv4; does NOT perform DNS resolution.
223 *
224 * On failure the destination buffer is zero-initialized so callers cannot accidentally
225 * consume uninitialized stack memory after a failed conversion.
226 *
227 * \param ipv4_address: NUL-terminated dotted-quad IPv4 address to convert (must not be null)
228 * \param out: pointer to a struct in_addr (or equivalently sized buffer) to fill
229 * (must not be null)
230 * \return: SOCK_SUCCESS on success, SOCK_INVALID_IP_ADDRESS on any malformed input
231 */
232 static SocketIpStatus addressToIp4(const char* const ipv4_address, void* const out);
233 /**
234 * \brief Protocol specific open implementation, called from open.
235 * \param socketDescriptor: (output) socket descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
236 * \return status of open
237 */
238 virtual SocketIpStatus openProtocol(SocketDescriptor& fd) = 0;
239 /**
240 * \brief Protocol specific implementation of send. Called directly with retry from send.
241 * \param socketDescriptor: socket descriptor to send to
242 * \param data: data to send
243 * \param size: size of data to send
244 * \return: size of data sent, or -1 on error.
245 */
246 virtual FwSignedSizeType sendProtocol(const SocketDescriptor& socketDescriptor,
247 const U8* const data,
248 const FwSizeType size) = 0;
249
250 /**
251 * \brief Protocol specific implementation of recv. Called directly with error handling from recv.
252 * \param socketDescriptor: socket descriptor to recv from
253 * \param data: data pointer to fill
254 * \param size: size of data buffer
255 * \return: size of data received, or -1 on error.
256 */
257 virtual FwSignedSizeType recvProtocol(const SocketDescriptor& socketDescriptor,
258 U8* const data,
259 const FwSizeType size) = 0;
260
261 /**
262 * \brief Handle zero return from recvProtocol
263 *
264 * This method is called when recvProtocol returns 0. The default implementation
265 * treats this as a disconnection (appropriate for TCP). Subclasses can override
266 * this to provide different behavior.
267 *
268 * @return SocketIpStatus Status to return from recv
269 */
270 virtual SocketIpStatus handleZeroReturn();
271
272 /**
273 * \brief setup the socket options of the input socket as defined in IpCfg.hpp
274 * \param socketFd: socket file descriptor
275 * \return status of setup options
276 */
277 SocketIpStatus setupSocketOptions(int socketFd);
278
279 U32 m_timeoutSeconds;
280 U32 m_timeoutMicroseconds;
281 U16 m_port; //!< IP address port used
282 char m_ipv4_address[SOCKET_MAX_IPV4_ADDRESS_SIZE]; //!< IPv4 address (dotted-quad "x.x.x.x")
283 };
284 } // namespace Drv
285
286 #endif /* DRV_SOCKETIPDRIVER_SOCKETHELPER_HPP_ */
287