simple-socket library 1.5.0
Loading...
Searching...
No Matches
InternetSocket.cpp
1#include "InternetSocket.h"
2#include "SockAddress.h"
3#include "TempFailure.h"
4
5#include <sys/socket.h>
6#include <poll.h>
7#include <fcntl.h>
8
9using namespace NET;
10
11namespace {
12
13class NonBlockingGuard
14{
15public:
16 explicit NonBlockingGuard(int fd) : m_fd(fd)
17 {
18 m_flags = ::fcntl( m_fd, F_GETFL, 0);
19 if( m_flags < 0)
20 throw SocketException("Unable to get file status (fcntl)");
21 if( ::fcntl( m_fd, F_SETFL, m_flags | O_NONBLOCK) < 0)
22 throw SocketException("Unable to set non-blocking mode (fcntl)");
23 }
24
25 ~NonBlockingGuard()
26 {
27 const int savedErrno = errno;
28 ::fcntl( m_fd, F_SETFL, m_flags);
29 errno = savedErrno;
30 }
31
32 NonBlockingGuard(const NonBlockingGuard&) = delete;
33 NonBlockingGuard& operator=(const NonBlockingGuard&) = delete;
34
35private:
36 int m_fd;
37 int m_flags;
38};
39
40AddressFamily socketAddressFamily(int fd)
41{
42 SockAddress addr;
43 if( ::getsockname( fd, addr.data(), addr.lengthPtr()) < 0)
44 throw SocketException("Unable to read socket name (getsockname)");
45
46 switch (addr.family())
47 {
48 case AF_INET: return AddressFamily::IPv4;
49 case AF_INET6: return AddressFamily::IPv6;
50 default:
51 throw SocketException("Unsupported Internet socket family", false);
52 }
53}
54
55}; // namespace
56
57InternetSocket::InternetSocket( AddressFamily family, int type, int protocol)
58: SimpleSocket( SockAddress::nativeFamily(family), type, protocol)
59, m_family( family)
60{}
61
63: SimpleSocket( sockfd)
64, m_family( socketAddressFamily(sockfd))
65{}
66
68{
69 const int value = enable;
70 if( ::setsockopt( m_socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)
71 throw SocketException("Failed to set address reuse (setsockopt)");
72}
73
74void InternetSocket::connect( std::string_view foreignAddress, unsigned short foreignPort)
75{
76 SockAddress addr( m_family, foreignAddress, foreignPort);
77
78 if( ::connect( m_socket, addr.data(), addr.length()) < 0)
79 throw SocketException("Connect failed (connect)");
80
81 m_peerDisconnected = false;
82}
83
84int InternetSocket::timedConnect( std::string_view foreignAddress, unsigned short foreignPort, int timeout)
85{
86 SockAddress addr( m_family, foreignAddress, foreignPort);
87
88 // Set O_NONBLOCK and restore blocking state on destruction
89 NonBlockingGuard nonBlocking( m_socket);
90
91 const auto throwError = []() {
92 throw SocketException("timedConnect failed (connect)");
93 };
94
95 // Asynchronous connection attempt
96 int ret = ::connect( m_socket, addr.data(), addr.length());
97 if( ret == 0) { // Already connected
98 m_peerDisconnected = false;
99 return 1;
100 }
101
102 // Did connect return an unexpected error?
103 if( errno != EINPROGRESS && errno != EWOULDBLOCK && errno != EINTR)
104 throwError();
105
106 // Wait for the connection to complete
107 struct pollfd poll;
108 poll.fd = m_socket;
109 poll.events = POLLOUT;
110
111 ret = TEMP_FAILURE_RETRY (::poll( &poll, 1, timeout));
112 if( ret < 0) throwError();
113 if( ret == 0) { // Timeout
114 errno = ETIMEDOUT;
115 return 0;
116 }
117
118 // Check for socket errors after poll success
119 int sockError = 0;
120 socklen_t errLen = sizeof(sockError);
121 ret = ::getsockopt( m_socket, SOL_SOCKET, SO_ERROR, &sockError, &errLen);
122 if( ret < 0) throwError();
123 if( sockError != 0) { // Socket error
124 errno = sockError;
125 throwError();
126 }
127
128 m_peerDisconnected = false;
129 return 1;
130}
131
132void InternetSocket::bind( unsigned short localPort /* = 0 */)
133{
134 SockAddress addr( m_family, localPort);
135
136 if( ::bind( m_socket, addr.data(), addr.length()) < 0)
137 throw SocketException("Set of local port failed (bind)");
138}
139
140void InternetSocket::bind( std::string_view localAddress, unsigned short localPort /* = 0 */)
141{
142 SockAddress addr( m_family, localAddress, localPort);
143
144 if( ::bind( m_socket, addr.data(), addr.length()) < 0)
145 throw SocketException("Set of local address and port failed (bind)");
146}
147
149{
150 SockAddress addr;
151
152 if( ::getsockname( m_socket, addr.data(), addr.lengthPtr()) < 0)
153 throw SocketException("Fetch of local address failed (getsockname)");
154
155 return addr.address();
156}
157
158unsigned short InternetSocket::getLocalPort() const
159{
160 SockAddress addr;
161
162 if( ::getsockname( m_socket, addr.data(), addr.lengthPtr()) < 0)
163 throw SocketException("Fetch of local port failed (getsockname)");
164
165 return addr.port();
166}
167
169{
170 SockAddress addr;
171
172 if( ::getpeername( m_socket, addr.data(), addr.lengthPtr()) < 0)
173 throw SocketException("Fetch of foreign address failed (getpeername)");
174
175 return addr.address();
176}
177
178unsigned short InternetSocket::getForeignPort() const
179{
180 SockAddress addr;
181
182 if( ::getpeername( m_socket, addr.data(), addr.lengthPtr()) < 0)
183 throw SocketException("Fetch of foreign port failed (getpeername)");
184
185 return addr.port();
186}
void bind(unsigned short localPort=0)
unsigned short getLocalPort() const
unsigned short getForeignPort() const
InternetSocket(int sockfd)
create socket from a SocketHandle returned by an accept() call
int timedConnect(std::string_view foreignAddress, unsigned short foreignPort, int timeout)
establish a connection with the given foreign address and port
void setReuseAddress(bool enable)
Enable or disable local address reuse.
void connect(std::string_view foreignAddress, unsigned short foreignPort)
establish a connection with the given foreign address and port
std::string getLocalAddress() const
std::string getForeignAddress() const
Signals a problem with the execution of a socket call.