GCC Code Coverage Report


Directory: ./
File: Drv/Ip/UdpSocket.cpp
Date: 2026-11-03 22:10:14
Exec Total Coverage
Lines: 0 121 0.0%
Functions: 0 12 0.0%
Branches: 0 75 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title UdpSocket.cpp
3 // \author mstarch
4 // \brief cpp file for UdpSocket 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 #include <Drv/Ip/UdpSocket.hpp>
13 #include <Fw/FPrimeBasicTypes.hpp>
14 #include <Fw/Logger/Logger.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <Fw/Types/StringUtils.hpp>
17
18 #include <cerrno>
19 #include <cstring>
20 #include <new>
21
22 namespace Drv {
23
24 ✗ UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
25 ✗ (void)::memset(&m_addr_send, 0, sizeof(m_addr_send));
26 ✗ (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
27 ✗ }
28
29 ✗ UdpSocket::~UdpSocket() = default;
30
31 ✗ SocketIpStatus UdpSocket::configure(const char* const ipv4_address,
32 const U16 port,
33 const U32 timeout_seconds,
34 const U32 timeout_microseconds) {
35 (void)ipv4_address;
36 (void)port;
37 (void)timeout_seconds;
38 (void)timeout_microseconds;
39 ✗ FW_ASSERT(false); // Must use configureSend and/or configureRecv
40 ✗ return SocketIpStatus::SOCK_INVALID_CALL;
41 }
42
43 ✗ SocketIpStatus UdpSocket::configureSend(const char* const ipv4_address,
44 const U16 port,
45 const U32 timeout_seconds,
46 const U32 timeout_microseconds) {
47 ✗ FW_ASSERT(ipv4_address != nullptr);
48 ✗ FW_ASSERT(this->isValidPort(port));
49 ✗ FW_ASSERT(timeout_microseconds < 1000000);
50 ✗ return IpSocket::configure(ipv4_address, port, timeout_seconds, timeout_microseconds);
51 }
52
53 ✗ SocketIpStatus UdpSocket::configureRecv(const char* const ipv4_address, const U16 port) {
54 ✗ FW_ASSERT(ipv4_address != nullptr);
55 ✗ FW_ASSERT(this->isValidPort(port));
56 ✗ FW_ASSERT(Fw::StringUtils::string_length(ipv4_address, static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE)) <
57 static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE));
58
59 // Initialize the receive address structure
60 ✗ (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
61 ✗ m_addr_recv.sin_family = AF_INET;
62 ✗ m_addr_recv.sin_port = htons(port);
63
64 // Convert IPv4 address (dotted-quad) to network-order in_addr
65 ✗ SocketIpStatus status = IpSocket::addressToIp4(ipv4_address, &m_addr_recv.sin_addr);
66 ✗ if (status != SOCK_SUCCESS) {
67 ✗ return status;
68 }
69
70 ✗ this->m_recv_configured = true;
71 ✗ return SOCK_SUCCESS;
72 }
73
74 ✗ U16 UdpSocket::getRecvPort() {
75 ✗ return ntohs(this->m_addr_recv.sin_port);
76 }
77
78 ✗ SocketIpStatus UdpSocket::bind(const int fd) {
79 ✗ FW_ASSERT(fd != -1);
80 ✗ struct sockaddr_in address = this->m_addr_recv;
81
82 // OS specific settings
83 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
84 address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
85 #endif
86 // UDP (for receiving) requires bind to an address to the socket
87 ✗ if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
88 ✗ return SOCK_FAILED_TO_BIND;
89 }
90
91 ✗ socklen_t size = sizeof(address);
92 ✗ const int socknameStatus = ::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size);
93 ✗ if (socknameStatus == -1) {
94 ✗ return SOCK_FAILED_TO_READ_BACK_PORT;
95 }
96
97 // Update m_addr_recv with the actual port assigned (for ephemeral port support)
98 ✗ this->m_addr_recv.sin_port = address.sin_port;
99
100 ✗ return SOCK_SUCCESS;
101 }
102
103 ✗ SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
104 ✗ if (this->m_port == 0 && !this->m_recv_configured) {
105 ✗ return SOCK_INVALID_CALL; // Neither send nor receive is configured
106 }
107
108 ✗ SocketIpStatus status = SOCK_SUCCESS;
109
110 // Initialize address structure to zero before use
111 struct sockaddr_in address;
112 ✗ (void)::memset(&address, 0, sizeof(address));
113
114 ✗ U16 port = this->m_port;
115 ✗ U16 recv_port = ntohs(this->m_addr_recv.sin_port);
116
117 // Acquire a socket, or return error
118 ✗ int socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);
119 ✗ if (socketFd == -1) {
120 ✗ return SOCK_FAILED_TO_GET_SOCKET;
121 }
122
123 // Apply the configured timeouts. This socket may send even when no send port was
124 // configured, because a send port of 0 means "reply to the source of the last
125 // datagram received", so the timeout must not be tied to `port != 0` below.
126 ✗ status = this->setupTimeouts(socketFd);
127 ✗ if (status != SOCK_SUCCESS) {
128 ✗ (void)::close(socketFd);
129 ✗ return status;
130 }
131
132 // May not be sending in all cases
133 ✗ if (port != 0) {
134 // Set up the address port and name
135 ✗ address.sin_family = AF_INET;
136 ✗ address.sin_port = htons(this->m_port);
137
138 // OS specific settings
139 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
140 address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
141 #endif
142
143 // Convert the configured IPv4 address (dotted-quad) to a network-order in_addr.
144 ✗ status = IpSocket::addressToIp4(this->m_ipv4_address, &(address.sin_addr));
145 ✗ if (status != SOCK_SUCCESS) {
146 ✗ Fw::Logger::log("Failed to parse IPv4 address %s: %d\n", this->m_ipv4_address, static_cast<I32>(status));
147 ✗ (void)::close(socketFd);
148 ✗ return status;
149 };
150
151 ✗ if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
152 ✗ (void)::close(socketFd);
153 ✗ return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
154 }
155
156 static_assert(sizeof(m_addr_send) == sizeof(address), "Send address must match the local address structure");
157 ✗ (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
158 }
159
160 // Only bind if configureRecv was called (including ephemeral)
161 ✗ if (this->m_recv_configured) {
162 ✗ status = this->bind(socketFd);
163
164 ✗ if (status != SOCK_SUCCESS) {
165 ✗ (void)::close(socketFd); // Closing FD as a retry will reopen send side
166 ✗ return status;
167 }
168 }
169
170 // Log message for UDP
171 char recv_addr[INET_ADDRSTRLEN];
172 ✗ const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
173 ✗ if (recv_addr_str == nullptr) {
174 ✗ (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
175 }
176
177 ✗ if ((port == 0) && (recv_port > 0)) {
178 ✗ Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
179 ✗ } else if ((port > 0) && (recv_port == 0)) {
180 ✗ Fw::Logger::log("Setup to only send udp at %s:%hu\n", this->m_ipv4_address, port);
181 ✗ } else if ((port > 0) && (recv_port > 0)) {
182 ✗ Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port,
183 ✗ this->m_ipv4_address, port);
184 }
185
186 ✗ FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
187 ✗ socketDescriptor.fd = socketFd;
188 ✗ return status;
189 }
190
191 ✗ FwSignedSizeType UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
192 const U8* const data,
193 const FwSizeType size) {
194 ✗ FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
195 ✗ FW_ASSERT((size == 0) || (data != nullptr)); // Data pointer should not be null for nonzero sizes
196
197 // In respond-to-sender mode the destination is learned from the first received
198 // packet; a send before then has nowhere to go and is a send error, not a bug
199 ✗ if (this->m_addr_send.sin_family == 0) {
200 ✗ errno = ENOTCONN;
201 ✗ return -1;
202 }
203
204 return static_cast<FwSignedSizeType>(
205 ✗ ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
206 ✗ reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
207 }
208
209 ✗ FwSignedSizeType UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
210 U8* const data,
211 const FwSizeType size) {
212 ✗ FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
213 ✗ FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
214 ✗ FW_ASSERT(data != nullptr); // Data pointer should not be null
215
216 // Initialize sender address structure to zero
217 struct sockaddr_in sender_addr;
218 ✗ (void)::memset(&sender_addr, 0, sizeof(sender_addr));
219
220 ✗ socklen_t sender_addr_len = sizeof(sender_addr);
221 FwSignedSizeType received = static_cast<FwSignedSizeType>(
222 ✗ ::recvfrom(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS,
223 reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
224 // If we have not configured a send port, set it to the source of the last received packet
225 ✗ if (received >= 0 && this->m_addr_send.sin_port == 0) {
226 ✗ this->m_addr_send = sender_addr;
227 ✗ this->m_port = ntohs(sender_addr.sin_port);
228 ✗ Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
229 }
230 ✗ return received;
231 }
232
233 ✗ SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
234 // Note: socketDescriptor.fd can be -1 in some test cases
235 ✗ FW_ASSERT((size == 0) || (data != nullptr));
236
237 // Special case for zero-length datagrams in UDP
238 ✗ if (size == 0) {
239 ✗ errno = 0;
240 ✗ FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
241 ✗ if (sent == -1) {
242 ✗ if (errno == EINTR) {
243 // For zero-length datagrams, we'll just try once more if interrupted
244 ✗ errno = 0;
245 ✗ sent = this->sendProtocol(socketDescriptor, data, 0);
246 }
247
248 ✗ if (sent == -1) {
249 ✗ if ((errno == EBADF) || (errno == ECONNRESET)) {
250 ✗ return SOCK_DISCONNECTED;
251 ✗ } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
252 ✗ return SOCK_INTERRUPTED_TRY_AGAIN;
253 } else {
254 ✗ return SOCK_SEND_ERROR;
255 }
256 }
257 }
258 // For zero-length datagrams in UDP, success is either 0 or a non-negative value
259 ✗ return SOCK_SUCCESS;
260 }
261
262 // For non-zero-length data, delegate to the base class implementation
263 ✗ return IpSocket::send(socketDescriptor, data, size);
264 }
265
266 ✗ SocketIpStatus UdpSocket::handleZeroReturn() {
267 // For UDP, a return of 0 from recvfrom means a 0-byte datagram was received.
268 // This is a success case for UDP, not a disconnection.
269 ✗ return SOCK_SUCCESS;
270 }
271
272 } // namespace Drv
273