F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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