GCC Code Coverage Report


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