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  // Now apply timeouts
155  status = this->setupTimeouts(socketFd);
156  if (status != SOCK_SUCCESS) {
157  ::close(socketFd);
158  return status;
159  }
160  FW_ASSERT(sizeof(this->m_addr_send) == sizeof(address), static_cast<FwAssertArgType>(sizeof(this->m_addr_send)),
161  static_cast<FwAssertArgType>(sizeof(address)));
162  (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
163  }
164 
165  // Only bind if configureRecv was called (including ephemeral)
166  if (this->m_recv_configured) {
167  status = this->bind(socketFd);
168 
169  if (status != SOCK_SUCCESS) {
170  (void)::close(socketFd); // Closing FD as a retry will reopen send side
171  return status;
172  }
173  }
174 
175  // Log message for UDP
176  char recv_addr[INET_ADDRSTRLEN];
177  const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
178  if (recv_addr_str == nullptr) {
179  (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
180  }
181 
182  if ((port == 0) && (recv_port > 0)) {
183  Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
184  } else if ((port > 0) && (recv_port == 0)) {
185  Fw::Logger::log("Setup to only send udp at %s:%hu\n", m_hostname, port);
186  } else if ((port > 0) && (recv_port > 0)) {
187  Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port, m_hostname, port);
188  }
189 
190  FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
191  socketDescriptor.fd = socketFd;
192  return status;
193 }
194 
195 I32 UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
196  FW_ASSERT(this->m_addr_send.sin_family != 0); // Make sure the address was previously setup
197  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
198  FW_ASSERT(data != nullptr); // Data pointer should not be null
199 
200  return static_cast<I32>(::sendto(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS,
201  reinterpret_cast<struct sockaddr*>(&this->m_addr_send),
202  sizeof(this->m_addr_send)));
203 }
204 
205 I32 UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
206  FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
207  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
208  FW_ASSERT(data != nullptr); // Data pointer should not be null
209 
210  // Initialize sender address structure to zero
211  struct sockaddr_in sender_addr;
212  (void)::memset(&sender_addr, 0, sizeof(sender_addr));
213 
214  socklen_t sender_addr_len = sizeof(sender_addr);
215  I32 received = static_cast<I32>(::recvfrom(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS,
216  reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
217  // If we have not configured a send port, set it to the source of the last received packet
218  if (received >= 0 && this->m_addr_send.sin_port == 0) {
219  this->m_addr_send = sender_addr;
220  this->m_port = ntohs(sender_addr.sin_port);
221  Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
222  }
223  return received;
224 }
225 
226 SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
227  // Note: socketDescriptor.fd can be -1 in some test cases
228  FW_ASSERT((size == 0) || (data != nullptr));
229 
230  // Special case for zero-length datagrams in UDP
231  if (size == 0) {
232  errno = 0;
233  I32 sent = this->sendProtocol(socketDescriptor, data, 0);
234  if (sent == -1) {
235  if (errno == EINTR) {
236  // For zero-length datagrams, we'll just try once more if interrupted
237  errno = 0;
238  sent = this->sendProtocol(socketDescriptor, data, 0);
239  }
240 
241  if (sent == -1) {
242  if ((errno == EBADF) || (errno == ECONNRESET)) {
243  return SOCK_DISCONNECTED;
244  } else {
245  return SOCK_SEND_ERROR;
246  }
247  }
248  }
249  // For zero-length datagrams in UDP, success is either 0 or a non-negative value
250  return SOCK_SUCCESS;
251  }
252 
253  // For non-zero-length data, delegate to the base class implementation
254  return IpSocket::send(socketDescriptor, data, size);
255 }
256 
258  // For UDP, a return of 0 from recvfrom means a 0-byte datagram was received.
259  // This is a success case for UDP, not a disconnection.
260  return SOCK_SUCCESS;
261 }
262 
263 } // 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
SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const U32 size) override
UDP-specific implementation of send that handles zero-length datagrams correctly. ...
Definition: UdpSocket.cpp:226
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
virtual SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const U32 size)
send data out the IP socket from the given buffer
Definition: IpSocket.cpp:132
SocketIpStatus setupTimeouts(int socketFd)
setup the socket timeout properties of the opened outgoing socket
Definition: IpSocket.cpp:69
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
SocketIpStatus configureRecv(const char *hostname, const U16 port)
configure the udp socket for incoming transmissions
Definition: UdpSocket.cpp:67
virtual ~UdpSocket()
to cleanup state created at instantiation
U16 m_port
IP address port used.
Definition: IpSocket.hpp:225
I32 sendProtocol(const SocketDescriptor &socketDescriptor, const U8 *const data, const U32 size) override
Protocol specific implementation of send. Called directly with retry from send.
Definition: UdpSocket.cpp:195
I32 recvProtocol(const SocketDescriptor &socketDescriptor, U8 *const data, const U32 size) override
Protocol specific implementation of recv. Called directly with error handling from recv...
Definition: UdpSocket.cpp:205
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
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
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:226
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:257
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