F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
UdpSocket.cpp
Go to the documentation of this file.
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 hostname,
50  const U16 port,
51  const U32 timeout_seconds,
52  const U32 timeout_microseconds) {
53  FW_ASSERT(0); // Must use configureSend and/or configureRecv
55 }
56 
57 SocketIpStatus UdpSocket::configureSend(const char* const hostname,
58  const U16 port,
59  const U32 timeout_seconds,
60  const U32 timeout_microseconds) {
61  FW_ASSERT(hostname != nullptr);
62  FW_ASSERT(this->isValidPort(port));
63  FW_ASSERT(timeout_microseconds < 1000000);
64  return IpSocket::configure(hostname, port, timeout_seconds, timeout_microseconds);
65 }
66 
67 SocketIpStatus UdpSocket::configureRecv(const char* hostname, const U16 port) {
68  FW_ASSERT(hostname != nullptr);
69  FW_ASSERT(this->isValidPort(port));
71 
72  // Initialize the receive address structure
73  (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
74  m_addr_recv.sin_family = AF_INET;
75  m_addr_recv.sin_port = htons(port);
76 
77  // Convert hostname to IP address
78  SocketIpStatus status = IpSocket::addressToIp4(hostname, &m_addr_recv.sin_addr);
79  if (status != SOCK_SUCCESS) {
80  return status;
81  }
82 
83  this->m_recv_configured = true;
84  return SOCK_SUCCESS;
85 }
86 
88  return ntohs(this->m_addr_recv.sin_port);
89 }
90 
92  FW_ASSERT(fd != -1);
93  struct sockaddr_in address = this->m_addr_recv;
94 
95  // OS specific settings
96 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
97  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
98 #endif
99  // UDP (for receiving) requires bind to an address to the socket
100  if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
101  return SOCK_FAILED_TO_BIND;
102  }
103 
104  socklen_t size = sizeof(address);
105  if (::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size) == -1) {
107  }
108 
109  // Update m_addr_recv with the actual port assigned (for ephemeral port support)
110  this->m_addr_recv.sin_port = address.sin_port;
111 
112  return SOCK_SUCCESS;
113 }
114 
116  if (this->m_port == 0 && !this->m_recv_configured) {
117  return SOCK_INVALID_CALL; // Neither send nor receive is configured
118  }
119 
120  SocketIpStatus status = SOCK_SUCCESS;
121  int socketFd = -1;
122 
123  // Initialize address structure to zero before use
124  struct sockaddr_in address;
125  (void)::memset(&address, 0, sizeof(address));
126 
127  U16 port = this->m_port;
128  U16 recv_port = ntohs(this->m_addr_recv.sin_port);
129 
130  // Acquire a socket, or return error
131  if ((socketFd = ::socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
133  }
134 
135  // May not be sending in all cases
136  if (port != 0) {
137  // Set up the address port and name
138  address.sin_family = AF_INET;
139  address.sin_port = htons(this->m_port);
140 
141  // OS specific settings
142 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
143  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
144 #endif
145 
146  // First IP address to socket sin_addr
147  status = IpSocket::addressToIp4(m_hostname, &(address.sin_addr));
148  if (status != SOCK_SUCCESS) {
149  Fw::Logger::log("Failed to resolve hostname %s: %d\n", m_hostname, static_cast<I32>(status));
150  ::close(socketFd);
151  return status;
152  };
153 
154  if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
155  ::close(socketFd);
157  }
158 
159  // Now apply timeouts
160  status = this->setupTimeouts(socketFd);
161  if (status != SOCK_SUCCESS) {
162  ::close(socketFd);
163  return status;
164  }
165  FW_ASSERT(sizeof(this->m_addr_send) == sizeof(address), static_cast<FwAssertArgType>(sizeof(this->m_addr_send)),
166  static_cast<FwAssertArgType>(sizeof(address)));
167  (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
168  }
169 
170  // Only bind if configureRecv was called (including ephemeral)
171  if (this->m_recv_configured) {
172  status = this->bind(socketFd);
173 
174  if (status != SOCK_SUCCESS) {
175  (void)::close(socketFd); // Closing FD as a retry will reopen send side
176  return status;
177  }
178  }
179 
180  // Log message for UDP
181  char recv_addr[INET_ADDRSTRLEN];
182  const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
183  if (recv_addr_str == nullptr) {
184  (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
185  }
186 
187  if ((port == 0) && (recv_port > 0)) {
188  Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
189  } else if ((port > 0) && (recv_port == 0)) {
190  Fw::Logger::log("Setup to only send udp at %s:%hu\n", m_hostname, port);
191  } else if ((port > 0) && (recv_port > 0)) {
192  Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port, m_hostname, port);
193  }
194 
195  FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
196  socketDescriptor.fd = socketFd;
197  return status;
198 }
199 
201  const U8* const data,
202  const FwSizeType size) {
203  FW_ASSERT(this->m_addr_send.sin_family != 0); // Make sure the address was previously setup
204  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
205  FW_ASSERT(data != nullptr); // Data pointer should not be null
206 
207  return static_cast<FwSignedSizeType>(
208  ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
209  reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
210 }
211 
213  U8* const data,
214  const FwSizeType size) {
215  FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
216  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
217  FW_ASSERT(data != nullptr); // Data pointer should not be null
218 
219  // Initialize sender address structure to zero
220  struct sockaddr_in sender_addr;
221  (void)::memset(&sender_addr, 0, sizeof(sender_addr));
222 
223  socklen_t sender_addr_len = sizeof(sender_addr);
224  FwSignedSizeType received = static_cast<FwSignedSizeType>(
225  ::recvfrom(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS,
226  reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
227  // If we have not configured a send port, set it to the source of the last received packet
228  if (received >= 0 && this->m_addr_send.sin_port == 0) {
229  this->m_addr_send = sender_addr;
230  this->m_port = ntohs(sender_addr.sin_port);
231  Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
232  }
233  return received;
234 }
235 
236 SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
237  // Note: socketDescriptor.fd can be -1 in some test cases
238  FW_ASSERT((size == 0) || (data != nullptr));
239 
240  // Special case for zero-length datagrams in UDP
241  if (size == 0) {
242  errno = 0;
243  FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
244  if (sent == -1) {
245  if (errno == EINTR) {
246  // For zero-length datagrams, we'll just try once more if interrupted
247  errno = 0;
248  sent = this->sendProtocol(socketDescriptor, data, 0);
249  }
250 
251  if (sent == -1) {
252  if ((errno == EBADF) || (errno == ECONNRESET)) {
253  return SOCK_DISCONNECTED;
254  } else {
255  return SOCK_SEND_ERROR;
256  }
257  }
258  }
259  // For zero-length datagrams in UDP, success is either 0 or a non-negative value
260  return SOCK_SUCCESS;
261  }
262 
263  // For non-zero-length data, delegate to the base class implementation
264  return IpSocket::send(socketDescriptor, data, size);
265 }
266 
268  // For UDP, a return of 0 from recvfrom means a 0-byte datagram was received.
269  // This is a success case for UDP, not a disconnection.
270  return SOCK_SUCCESS;
271 }
272 
273 } // namespace Drv
static SocketIpStatus addressToIp4(const char *address, void *ip4)
converts a given address in dot form x.x.x.x to an ip address. ONLY works for IPv4.
Definition: IpSocket.cpp:86
Failed to send after configured retries.
Definition: IpSocket.hpp:42
U16 getRecvPort()
get the port being received on
Definition: UdpSocket.cpp:87
virtual bool isValidPort(U16 port)
Check if the given port is valid for the socket.
Definition: IpSocket.cpp:65
Failed to read socket with disconnect.
Definition: IpSocket.hpp:38
Socket open failed.
Definition: IpSocket.hpp:31
SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size) override
UDP-specific implementation of send that handles zero-length datagrams correctly. ...
Definition: UdpSocket.cpp:236
Failed to configure socket.
Definition: IpSocket.hpp:35
PlatformSizeType FwSizeType
SocketIpStatus setupTimeouts(int socketFd)
setup the socket timeout properties of the opened outgoing socket
Definition: IpSocket.cpp:69
FwSignedSizeType sendProtocol(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size) override
Protocol specific implementation of send. Called directly with retry from send.
Definition: UdpSocket.cpp:200
static void log(const char *format,...)
log a formated string with supplied arguments
Definition: Logger.cpp:21
int fd
Used for all sockets to track the communication file descriptor.
Definition: IpSocket.hpp:22
PlatformSignedSizeType FwSignedSizeType
SocketIpStatus configureRecv(const char *hostname, const U16 port)
configure the udp socket for incoming transmissions
Definition: UdpSocket.cpp:67
virtual SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size)
send data out the IP socket from the given buffer
Definition: IpSocket.cpp:132
virtual ~UdpSocket()
to cleanup state created at instantiation
U16 m_port
IP address port used.
Definition: IpSocket.hpp:236
Socket operation successful.
Definition: IpSocket.hpp:30
UdpSocket()
Constructor for client socket udp implementation.
Definition: UdpSocket.cpp:42
SocketIpStatus bind(const int fd)
bind the UDP to a port such that it can receive packets at the previously configured port ...
Definition: UdpSocket.cpp:91
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:7
FwSignedSizeType recvProtocol(const SocketDescriptor &socketDescriptor, U8 *const data, const FwSizeType size) override
Protocol specific implementation of recv. Called directly with error handling from recv...
Definition: UdpSocket.cpp:212
SocketIpStatus configureSend(const char *hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds)
configure the udp socket for outgoing transmissions
Definition: UdpSocket.cpp:57
SocketIpStatus setupSocketOptions(int socketFd)
setup the socket options of the input socket as defined in IpCfg.hpp
Definition: IpSocket.cpp:217
Failed to read back port from connection.
Definition: IpSocket.hpp:44
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
virtual SocketIpStatus configure(const char *hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds)
configure the ip socket with host and transmission timeouts
Definition: IpSocket.cpp:51
Operation is invalid.
Definition: IpSocket.hpp:48
void close(const SocketDescriptor &socketDescriptor)
closes the socket
Definition: IpSocket.cpp:107
char m_hostname[SOCKET_MAX_HOSTNAME_SIZE]
Hostname to supply.
Definition: IpSocket.hpp:237
SocketIpStatus
Status enumeration for socket return values.
Definition: IpSocket.hpp:29
Helper base-class for setting up Berkeley sockets.
Definition: IpSocket.hpp:57
Failed to bind to socket.
Definition: IpSocket.hpp:39
#define FW_ASSERT(...)
Definition: Assert.hpp:14
SocketIpStatus handleZeroReturn() override
Handle zero return from recvProtocol for UDP.
Definition: UdpSocket.cpp:267
SocketIpStatus configure(const char *hostname, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds) override
configure is disabled
Definition: UdpSocket.cpp:49
SocketIpStatus openProtocol(SocketDescriptor &socketDescriptor) override
udp specific implementation for opening a socket.
Definition: UdpSocket.cpp:115
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
Definition: StringUtils.cpp:24