GCC Code Coverage Report


Directory: ./
File: Drv/Ip/UdpSocket.cpp
Date: 2026-11-03 21:15:54
Exec Total Coverage
Lines: 98 124 79.0%
Functions: 10 12 83.3%
Branches: 52 85 61.2%

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 832 UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
25
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 832 times.
832 (void)::memset(&m_addr_send, 0, sizeof(m_addr_send));
26
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 832 times.
832 (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
27 832 }
28
29 1664 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 627 SocketIpStatus UdpSocket::configureSend(const char* const ipv4_address,
44 const U16 port,
45 const U32 timeout_seconds,
46 const U32 timeout_microseconds) {
47 627 FW_ASSERT(ipv4_address != nullptr);
48 627 FW_ASSERT(this->isValidPort(port));
49 627 FW_ASSERT(timeout_microseconds < 1000000);
50 627 return IpSocket::configure(ipv4_address, port, timeout_seconds, timeout_microseconds);
51 }
52
53 726 SocketIpStatus UdpSocket::configureRecv(const char* const ipv4_address, const U16 port) {
54 726 FW_ASSERT(ipv4_address != nullptr);
55 726 FW_ASSERT(this->isValidPort(port));
56 726 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
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 726 times.
726 (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
61 726 m_addr_recv.sin_family = AF_INET;
62 726 m_addr_recv.sin_port = htons(port);
63
64 // Convert IPv4 address (dotted-quad) to network-order in_addr
65 726 SocketIpStatus status = IpSocket::addressToIp4(ipv4_address, &m_addr_recv.sin_addr);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 726 times.
726 if (status != SOCK_SUCCESS) {
67 ✗ return status;
68 }
69
70 726 this->m_recv_configured = true;
71 726 return SOCK_SUCCESS;
72 }
73
74 ✗ U16 UdpSocket::getRecvPort() {
75 ✗ return ntohs(this->m_addr_recv.sin_port);
76 }
77
78 834 SocketIpStatus UdpSocket::bind(const int fd) {
79 834 FW_ASSERT(fd != -1);
80 834 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
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 834 times.
834 if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
88 ✗ return SOCK_FAILED_TO_BIND;
89 }
90
91 834 socklen_t size = sizeof(address);
92 834 const int socknameStatus = ::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size);
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 834 times.
834 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 834 this->m_addr_recv.sin_port = address.sin_port;
99
100 834 return SOCK_SUCCESS;
101 }
102
103 1037 SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
104
4/6
✓ Branch 5 taken 205 times.
✓ Branch 6 taken 832 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 205 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 205 times.
1037 if (this->m_port == 0 && !this->m_recv_configured) {
105 ✗ return SOCK_INVALID_CALL; // Neither send nor receive is configured
106 }
107
108 1037 SocketIpStatus status = SOCK_SUCCESS;
109
110 // Initialize address structure to zero before use
111 1037 struct sockaddr_in address;
112 1037 (void)::memset(&address, 0, sizeof(address));
113
114 1037 U16 port = this->m_port;
115 1037 U16 recv_port = ntohs(this->m_addr_recv.sin_port);
116
117 // Acquire a socket, or return error
118 1037 int socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1037 times.
1037 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
1/1
✓ Branch 5 taken 1037 times.
1037 status = this->setupTimeouts(socketFd);
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1037 times.
1037 if (status != SOCK_SUCCESS) {
128 ✗ (void)::close(socketFd);
129 ✗ return status;
130 }
131
132 // May not be sending in all cases
133
2/2
✓ Branch 0 taken 832 times.
✓ Branch 1 taken 205 times.
1037 if (port != 0) {
134 // Set up the address port and name
135 832 address.sin_family = AF_INET;
136 832 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
1/1
✓ Branch 3 taken 832 times.
832 status = IpSocket::addressToIp4(this->m_ipv4_address, &(address.sin_addr));
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 832 times.
832 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
2/3
✓ Branch 5 taken 832 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 832 times.
832 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 832 (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
158 }
159
160 // Only bind if configureRecv was called (including ephemeral)
161
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 1037 times.
✓ Branch 5 taken 834 times.
✓ Branch 6 taken 203 times.
1037 if (this->m_recv_configured) {
162
1/1
✓ Branch 4 taken 834 times.
834 status = this->bind(socketFd);
163
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 834 times.
834 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 1037 char recv_addr[INET_ADDRSTRLEN];
172 1037 const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1037 times.
1037 if (recv_addr_str == nullptr) {
174 ✗ (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
175 }
176
177
3/4
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 832 times.
✓ Branch 2 taken 205 times.
✗ Branch 3 not taken.
1037 if ((port == 0) && (recv_port > 0)) {
178
1/1
✓ Branch 1 taken 205 times.
205 Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
179
3/4
✓ Branch 0 taken 832 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 204 times.
✓ Branch 3 taken 628 times.
832 } else if ((port > 0) && (recv_port == 0)) {
180
1/1
✓ Branch 3 taken 204 times.
204 Fw::Logger::log("Setup to only send udp at %s:%hu\n", this->m_ipv4_address, port);
181
2/4
✓ Branch 0 taken 628 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
628 } else if ((port > 0) && (recv_port > 0)) {
182 1256 Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port,
183
1/1
✓ Branch 3 taken 628 times.
628 this->m_ipv4_address, port);
184 }
185
186 1037 FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
187 1037 socketDescriptor.fd = socketFd;
188 1037 return status;
189 }
190
191 631 FwSignedSizeType UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
192 const U8* const data,
193 const FwSizeType size) {
194 631 FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
195 631 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
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 631 times.
631 if (this->m_addr_send.sin_family == 0) {
200 ✗ errno = ENOTCONN;
201 ✗ return -1;
202 }
203
204 return static_cast<FwSignedSizeType>(
205 631 ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
206 1262 reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
207 }
208
209 642 FwSignedSizeType UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
210 U8* const data,
211 const FwSizeType size) {
212 642 FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
213 642 FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
214 642 FW_ASSERT(data != nullptr); // Data pointer should not be null
215
216 // Initialize sender address structure to zero
217 642 struct sockaddr_in sender_addr;
218 642 (void)::memset(&sender_addr, 0, sizeof(sender_addr));
219
220 642 socklen_t sender_addr_len = sizeof(sender_addr);
221 FwSignedSizeType received = static_cast<FwSignedSizeType>(
222
1/1
✓ Branch 2 taken 642 times.
642 ::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
4/4
✓ Branch 0 taken 633 times.
✓ Branch 1 taken 9 times.
✓ Branch 6 taken 204 times.
✓ Branch 7 taken 429 times.
642 if (received >= 0 && this->m_addr_send.sin_port == 0) {
226 204 this->m_addr_send = sender_addr;
227 204 this->m_port = ntohs(sender_addr.sin_port);
228
1/1
✓ Branch 6 taken 204 times.
204 Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
229 }
230 1284 return received;
231 }
232
233 632 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 632 FW_ASSERT((size == 0) || (data != nullptr));
236
237 // Special case for zero-length datagrams in UDP
238
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 630 times.
632 if (size == 0) {
239 2 errno = 0;
240 2 FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
241
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (sent == -1) {
242
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 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
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (sent == -1) {
249
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if ((errno == EBADF) || (errno == ECONNRESET)) {
250 ✗ return SOCK_DISCONNECTED;
251
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
252 1 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 1 return SOCK_SUCCESS;
260 }
261
262 // For non-zero-length data, delegate to the base class implementation
263 630 return IpSocket::send(socketDescriptor, data, size);
264 }
265
266 3 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 3 return SOCK_SUCCESS;
270 }
271
272 } // namespace Drv
273