F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
IpSocket.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title IpSocket.cpp
3 // \author mstarch, crsmith
4 // \brief cpp file for IpSocket 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 <sys/time.h>
13 #include <Drv/Ip/IpSocket.hpp>
14 #include <Fw/FPrimeBasicTypes.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <Fw/Types/StringUtils.hpp>
17 #include <cstring>
18 
19 // This implementation has primarily implemented to isolate
20 // the socket interface from the F' Fw::Buffer class.
21 // There is a macro in VxWorks (m_data) that collides with
22 // the m_data member in Fw::Buffer.
23 
24 #ifdef TGT_OS_TYPE_VXWORKS
25 #include <errnoLib.h>
26 #include <fioLib.h>
27 #include <hostLib.h>
28 #include <inetLib.h>
29 #include <ioLib.h>
30 #include <sockLib.h>
31 #include <socket.h>
32 #include <sysLib.h>
33 #include <taskLib.h>
34 #include <vxWorks.h>
35 #include <cstring>
36 #elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
37 #include <arpa/inet.h>
38 #include <sys/socket.h>
39 #include <unistd.h>
40 #include <cerrno>
41 #else
42 #error OS not supported for IP Socket Communications
43 #endif
44 
45 namespace Drv {
46 
47 IpSocket::IpSocket() : m_timeoutSeconds(0), m_timeoutMicroseconds(0), m_port(0) {
48  ::memset(m_hostname, 0, sizeof(m_hostname));
49 }
50 
51 SocketIpStatus IpSocket::configure(const char* const hostname,
52  const U16 port,
53  const U32 timeout_seconds,
54  const U32 timeout_microseconds) {
55  FW_ASSERT(timeout_microseconds < 1000000, static_cast<FwAssertArgType>(timeout_microseconds));
56  FW_ASSERT(this->isValidPort(port), static_cast<FwAssertArgType>(port));
57  FW_ASSERT(hostname != nullptr);
58  this->m_timeoutSeconds = timeout_seconds;
59  this->m_timeoutMicroseconds = timeout_microseconds;
60  this->m_port = port;
61  (void)Fw::StringUtils::string_copy(this->m_hostname, hostname, static_cast<FwSizeType>(SOCKET_MAX_HOSTNAME_SIZE));
62  return SOCK_SUCCESS;
63 }
64 
65 bool IpSocket::isValidPort(U16 port) {
66  return true;
67 }
68 
70 // Get the IP address from host
71 #ifdef TGT_OS_TYPE_VXWORKS
72  // No timeouts set on Vxworks
73 #else
74  // Set timeout socket option
75  struct timeval timeout;
76  timeout.tv_sec = static_cast<time_t>(this->m_timeoutSeconds);
77  timeout.tv_usec = static_cast<suseconds_t>(this->m_timeoutMicroseconds);
78  // set socket write to timeout after 1 sec
79  if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout)) < 0) {
81  }
82 #endif
83  return SOCK_SUCCESS;
84 }
85 
86 SocketIpStatus IpSocket::addressToIp4(const char* address, void* ip4) {
87  FW_ASSERT(address != nullptr);
88  FW_ASSERT(ip4 != nullptr);
89  // Get the IP address from host
90 #ifdef TGT_OS_TYPE_VXWORKS
91  int ip = inet_addr(address);
92  if (ip == ERROR) {
94  }
95  // from sin_addr, which has one struct
96  // member s_addr, which is unsigned int
97  *reinterpret_cast<unsigned long*>(ip4) = ip;
98 #else
99  // First IP address to socket sin_addr
100  if (not ::inet_pton(AF_INET, address, ip4)) {
102  };
103 #endif
104  return SOCK_SUCCESS;
105 }
106 
107 void IpSocket::close(const SocketDescriptor& socketDescriptor) {
108  (void)::close(socketDescriptor.fd);
109 }
110 
111 void IpSocket::shutdown(const SocketDescriptor& socketDescriptor) {
112  errno = 0;
113  int status = ::shutdown(socketDescriptor.fd, SHUT_RDWR);
114  // If shutdown fails, go straight to the hard-shutdown
115  if (status != 0) {
116  this->close(socketDescriptor);
117  }
118 }
119 
121  SocketIpStatus status = SOCK_SUCCESS;
122  errno = 0;
123  // Open a TCP socket for incoming commands, and outgoing data if not using UDP
124  status = this->openProtocol(socketDescriptor);
125  if (status != SOCK_SUCCESS) {
126  socketDescriptor.fd = -1;
127  return status;
128  }
129  return status;
130 }
131 
132 SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
133  FW_ASSERT(data != nullptr);
134  FW_ASSERT(size > 0);
135 
136  FwSizeType total = 0;
137  FwSignedSizeType sent = 0;
138  // Attempt to send out data and retry as necessary
139  for (FwSizeType i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) {
140  errno = 0;
141  // Send using my specific protocol
142  sent = this->sendProtocol(socketDescriptor, data + total, size - total);
143  // Error is EINTR or timeout just try again
144  if (((sent == -1) && (errno == EINTR)) || (sent == 0)) {
145  continue;
146  }
147  // Error bad file descriptor is a close along with reset
148  else if ((sent == -1) && ((errno == EBADF) || (errno == ECONNRESET))) {
149  return SOCK_DISCONNECTED;
150  }
151  // Error returned, and it wasn't an interrupt nor a disconnect
152  else if (sent == -1) {
153  return SOCK_SEND_ERROR;
154  }
155  FW_ASSERT(sent > 0, static_cast<FwAssertArgType>(sent));
156  total += static_cast<FwSizeType>(sent);
157  }
158  // Failed to retry enough to send all data
159  if (total < size) {
161  }
162  // Ensure we sent everything
163  FW_ASSERT(total == size, static_cast<FwAssertArgType>(total), static_cast<FwAssertArgType>(size));
164  return SOCK_SUCCESS;
165 }
166 
167 SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data, FwSizeType& req_read) {
168  // TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized
169  // socketDescriptor
170  // FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
171  FW_ASSERT(data != nullptr);
172 
173  FwSignedSizeType bytes_received_or_status; // Stores the return value from recvProtocol
174 
175  // Loop primarily for EINTR. Other conditions should lead to an earlier exit.
176  for (FwSizeType i = 0; i < SOCKET_MAX_ITERATIONS; i++) {
177  errno = 0;
178  // Pass the current value of req_read (max buffer size) to recvProtocol.
179  // recvProtocol returns bytes read or -1 on error.
180  bytes_received_or_status = this->recvProtocol(socketDescriptor, data, req_read);
181 
182  if (bytes_received_or_status > 0) {
183  // Successfully read data
184  req_read = static_cast<FwSizeType>(bytes_received_or_status);
185  return SOCK_SUCCESS;
186  } else if (bytes_received_or_status == 0) {
187  // Handle zero return based on protocol-specific behavior
188  req_read = 0;
189  return this->handleZeroReturn();
190  } else { // bytes_received_or_status == -1, an error occurred
191  if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
192  // Non-blocking socket would block, or SO_RCVTIMEO timeout occurred.
193  req_read = 0;
194  return SOCK_NO_DATA_AVAILABLE;
195  } else if ((errno == ECONNRESET) || (errno == EBADF)) {
196  // Connection reset or bad file descriptor.
197  req_read = 0;
198  return SOCK_DISCONNECTED; // Or a more specific error like SOCK_READ_ERROR
199  } else {
200  // Other socket read error.
201  req_read = 0;
202  return SOCK_READ_ERROR;
203  }
204  }
205  }
206  // If the loop completes, it means SOCKET_MAX_ITERATIONS of EINTR occurred.
207  req_read = 0;
209 }
210 
212  // For TCP (which IpSocket primarily serves as a base for, or when not overridden),
213  // a return of 0 from ::recv means the peer has performed an orderly shutdown.
214  return SOCK_DISCONNECTED;
215 }
216 
218  // Iterate over the socket options and set them
219  for (const auto& options : IP_SOCKET_OPTIONS) {
220  int status = 0;
221  if (options.type == SOCK_OPT_INT) {
222  status = setsockopt(socketFd, options.level, options.option, &options.value.intVal,
223  sizeof(options.value.intVal));
224  } else {
225  status = setsockopt(socketFd, options.level, options.option, &options.value.sizeVal,
226  sizeof(options.value.sizeVal));
227  }
228  if (status) {
230  }
231  }
232  return SOCK_SUCCESS;
233 }
234 
235 } // 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
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
Interrupted status for retries.
Definition: IpSocket.hpp:36
Failed to configure socket.
Definition: IpSocket.hpp:35
PlatformSizeType FwSizeType
SocketIpStatus recv(const SocketDescriptor &fd, U8 *const data, FwSizeType &size)
receive data from the IP socket from the given buffer
Definition: IpSocket.cpp:167
Operation failed.
Definition: Os.hpp:27
SocketIpStatus setupTimeouts(int socketFd)
setup the socket timeout properties of the opened outgoing socket
Definition: IpSocket.cpp:69
int fd
Used for all sockets to track the communication file descriptor.
Definition: IpSocket.hpp:22
PlatformSignedSizeType FwSignedSizeType
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
Bad IP address supplied.
Definition: IpSocket.hpp:33
virtual SocketIpStatus openProtocol(SocketDescriptor &fd)=0
Protocol specific open implementation, called from open.
void shutdown(const SocketDescriptor &socketDescriptor)
shutdown the socket
Definition: IpSocket.cpp:111
virtual FwSignedSizeType sendProtocol(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size)=0
Protocol specific implementation of send. Called directly with retry from send.
U16 m_port
IP address port used.
Definition: IpSocket.hpp:236
Socket operation successful.
Definition: IpSocket.hpp:30
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:7
Failed to read socket.
Definition: IpSocket.hpp:37
SocketIpStatus setupSocketOptions(int socketFd)
setup the socket options of the input socket as defined in IpCfg.hpp
Definition: IpSocket.cpp:217
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
static const IpSocketOptions IP_SOCKET_OPTIONS[]
Definition: IpCfg.hpp:80
virtual SocketIpStatus handleZeroReturn()
Handle zero return from recvProtocol.
Definition: IpSocket.cpp:211
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
virtual FwSignedSizeType recvProtocol(const SocketDescriptor &socketDescriptor, U8 *const data, const FwSizeType size)=0
Protocol specific implementation of recv. Called directly with error handling from recv...
SocketIpStatus open(SocketDescriptor &socketDescriptor)
open the IP socket for communications
Definition: IpSocket.cpp:120
SocketIpStatus
Status enumeration for socket return values.
Definition: IpSocket.hpp:29
U32 m_timeoutMicroseconds
Definition: IpSocket.hpp:235
No data available or read operation would block.
Definition: IpSocket.hpp:45
#define FW_ASSERT(...)
Definition: Assert.hpp:14
U32 m_timeoutSeconds
Definition: IpSocket.hpp:234