GCC Code Coverage Report


Directory: ./
File: Drv/Ip/UdpSocket.cpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 0 119 0.0%
Functions: 0 12 0.0%
Branches: 0 71 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 #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 UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
43 (void)::memset(&m_addr_send, 0, sizeof(m_addr_send));
44 (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
45 }
46
47 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 SocketIpStatus UdpSocket::configureSend(const char* const ipv4_address,
62 const U16 port,
63 const U32 timeout_seconds,
64 const U32 timeout_microseconds) {
65 FW_ASSERT(ipv4_address != nullptr);
66 FW_ASSERT(this->isValidPort(port));
67 FW_ASSERT(timeout_microseconds < 1000000);
68 return IpSocket::configure(ipv4_address, port, timeout_seconds, timeout_microseconds);
69 }
70
71 SocketIpStatus UdpSocket::configureRecv(const char* const ipv4_address, const U16 port) {
72 FW_ASSERT(ipv4_address != nullptr);
73 FW_ASSERT(this->isValidPort(port));
74 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 (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
79 m_addr_recv.sin_family = AF_INET;
80 m_addr_recv.sin_port = htons(port);
81
82 // Convert IPv4 address (dotted-quad) to network-order in_addr
83 SocketIpStatus status = IpSocket::addressToIp4(ipv4_address, &m_addr_recv.sin_addr);
84 if (status != SOCK_SUCCESS) {
85 return status;
86 }
87
88 this->m_recv_configured = true;
89 return SOCK_SUCCESS;
90 }
91
92 U16 UdpSocket::getRecvPort() {
93 return ntohs(this->m_addr_recv.sin_port);
94 }
95
96 SocketIpStatus UdpSocket::bind(const int fd) {
97 FW_ASSERT(fd != -1);
98 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 if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
106 return SOCK_FAILED_TO_BIND;
107 }
108
109 socklen_t size = sizeof(address);
110 const int socknameStatus = ::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size);
111 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 this->m_addr_recv.sin_port = address.sin_port;
117
118 return SOCK_SUCCESS;
119 }
120
121 SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
122 if (this->m_port == 0 && !this->m_recv_configured) {
123 return SOCK_INVALID_CALL; // Neither send nor receive is configured
124 }
125
126 SocketIpStatus status = SOCK_SUCCESS;
127
128 // Initialize address structure to zero before use
129 struct sockaddr_in address;
130 (void)::memset(&address, 0, sizeof(address));
131
132 U16 port = this->m_port;
133 U16 recv_port = ntohs(this->m_addr_recv.sin_port);
134
135 // Acquire a socket, or return error
136 int socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);
137 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 status = this->setupTimeouts(socketFd);
145 if (status != SOCK_SUCCESS) {
146 (void)::close(socketFd);
147 return status;
148 }
149
150 // May not be sending in all cases
151 if (port != 0) {
152 // Set up the address port and name
153 address.sin_family = AF_INET;
154 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 status = IpSocket::addressToIp4(this->m_ipv4_address, &(address.sin_addr));
163 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 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 (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
176 }
177
178 // Only bind if configureRecv was called (including ephemeral)
179 if (this->m_recv_configured) {
180 status = this->bind(socketFd);
181
182 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 char recv_addr[INET_ADDRSTRLEN];
190 const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
191 if (recv_addr_str == nullptr) {
192 (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
193 }
194
195 if ((port == 0) && (recv_port > 0)) {
196 Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
197 } else if ((port > 0) && (recv_port == 0)) {
198 Fw::Logger::log("Setup to only send udp at %s:%hu\n", this->m_ipv4_address, port);
199 } else if ((port > 0) && (recv_port > 0)) {
200 Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port,
201 this->m_ipv4_address, port);
202 }
203
204 FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
205 socketDescriptor.fd = socketFd;
206 return status;
207 }
208
209 FwSignedSizeType UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
210 const U8* const data,
211 const FwSizeType size) {
212 FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
213 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 if (this->m_addr_send.sin_family == 0) {
218 errno = ENOTCONN;
219 return -1;
220 }
221
222 return static_cast<FwSignedSizeType>(
223 ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
224 reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
225 }
226
227 FwSignedSizeType UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
228 U8* const data,
229 const FwSizeType size) {
230 FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
231 FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
232 FW_ASSERT(data != nullptr); // Data pointer should not be null
233
234 // Initialize sender address structure to zero
235 struct sockaddr_in sender_addr;
236 (void)::memset(&sender_addr, 0, sizeof(sender_addr));
237
238 socklen_t sender_addr_len = sizeof(sender_addr);
239 FwSignedSizeType received = static_cast<FwSignedSizeType>(
240 ::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 if (received >= 0 && this->m_addr_send.sin_port == 0) {
244 this->m_addr_send = sender_addr;
245 this->m_port = ntohs(sender_addr.sin_port);
246 Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
247 }
248 return received;
249 }
250
251 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 FW_ASSERT((size == 0) || (data != nullptr));
254
255 // Special case for zero-length datagrams in UDP
256 if (size == 0) {
257 errno = 0;
258 FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
259 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 return SOCK_SUCCESS;
276 }
277
278 // For non-zero-length data, delegate to the base class implementation
279 return IpSocket::send(socketDescriptor, data, size);
280 }
281
282 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 return SOCK_SUCCESS;
286 }
287
288 } // namespace Drv
289