GCC Code Coverage Report


Directory: ./
File: Drv/Ip/UdpSocket.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 93 122 76.2%
Functions: 10 12 83.3%
Branches: 46 81 56.8%

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