scclib
Stable Cloud Computing C++ Library
socket.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <net/socket.h>
32 #include <cerrno>
33 #include <stdexcept>
34 #include <system_error>
35 #include <sstream>
36 #include <string>
37 #include <ostream>
38 #include <cassert>
39 #include <util/safe_clib.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <sys/fcntl.h>
43 
49 using namespace scc::net;
50 
51 std::ostream& operator<<(std::ostream& os, const SockaddrBase& sa)
52 {
53  return os.write(sa.str().c_str(), sa.str().size());
54 }
55 
56 SocketBase::SocketBase(int domain, int stype, int proto) : m_fd{-1}
57 {
58  m_fd = ::socket(domain, stype, proto);
59  if (m_fd == -1)
60  {
61  std::stringstream st;
62  st << "socket()";
63  throw std::system_error(errno, std::system_category(), st.str());
64  }
65 }
66 
67 SocketBase::~SocketBase()
68 {
69  close();
70 }
71 
73 {
74  if (m_fd != -1)
75  {
77  m_fd = -1;
78  }
79 }
80 
81 void SocketBase::reset(int domain, int stype, int proto)
82 {
83  close();
84 
85  m_fd = ::socket(domain, stype, proto);
86  if (m_fd == -1)
87  {
88  std::stringstream st;
89  st << "socket() from reset()";
90  throw std::system_error(errno, std::system_category(), st.str());
91  }
92 }
93 
94 std::error_code SocketBase::error_code()
95 {
96  int x;
97 
98  socklen_t len = sizeof(x);
99  if (::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &x, &len))
100  {
101  std::stringstream st;
102  st << "getsockopt(SO_ERROR)";
103  throw std::system_error(errno, std::system_category(), st.str());
104  }
105  return std::error_code(x, std::system_category());
106 }
107 
108 void SocketBase::recv_bufsize(unsigned s)
109 {
110  unsigned x = s;
111 
112  socklen_t len = sizeof(x);
113  if (::setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, &x, len))
114  {
115  std::stringstream st;
116  st << "setsockopt(SO_RCVBUF)";
117  throw std::system_error(errno, std::system_category(), st.str());
118  }
119 }
120 
122 {
123  unsigned x;
124 
125  socklen_t len = sizeof(x);
126  if (::getsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, &x, &len))
127  {
128  std::stringstream st;
129  st << "getsockopt(SO_RCVBUF)";
130  throw std::system_error(errno, std::system_category(), st.str());
131  }
132  return x;
133 }
134 
135 void SocketBase::send_bufsize(unsigned s)
136 {
137  unsigned x = s;
138 
139  socklen_t len = sizeof(x);
140  if (::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &x, len))
141  {
142  std::stringstream st;
143  st << "setsockopt(SO_SNDBUF)";
144  throw std::system_error(errno, std::system_category(), st.str());
145  }
146 }
147 
149 {
150  unsigned x;
151 
152  socklen_t len = sizeof(x);
153  if (::getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &x, &len))
154  {
155  std::stringstream st;
156  st << "getsockopt(SO_SNDBUF)";
157  throw std::system_error(errno, std::system_category(), st.str());
158  }
159  return x;
160 }
161 
163 {
164  int x = r ? 1 : 0;
165 
166  socklen_t len = sizeof(x);
167  if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &x, len))
168  {
169  std::stringstream st;
170  st << "setsockopt(SO_REUSEADDR)";
171  throw std::system_error(errno, std::system_category(), st.str());
172  }
173 }
174 
176 {
177  int x = r ? 1 : 0;
178 
179  socklen_t len = sizeof(x);
180  if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT, &x, len))
181  {
182  std::stringstream st;
183  st << "setsockopt(SO_REUSEPORT)";
184  throw std::system_error(errno, std::system_category(), st.str());
185  }
186 }
187 
189 {
190  int flags = ::fcntl(m_fd, F_GETFL, 0);
191  if (flags == -1)
192  {
193  std::stringstream st;
194  st << "fcntl(F_GETFL)";
195  throw std::system_error(errno, std::system_category(), st.str());
196  }
197  if (b)
198  {
199  if (::fcntl(m_fd, F_SETFL, flags | O_NONBLOCK) == -1)
200  {
201  std::stringstream st;
202  st << "fcntl(F_SETFL)";
203  throw std::system_error(errno, std::system_category(), st.str());
204  }
205  }
206  else
207  {
208  if (::fcntl(m_fd, F_SETFL, flags & ~O_NONBLOCK) == -1)
209  {
210  std::stringstream st;
211  st << "fcntl(F_SETFL)";
212  throw std::system_error(errno, std::system_category(), st.str());
213  }
214  }
215 }
216 
217 void SocketBase::send_timeout(std::chrono::milliseconds t)
218 {
219  timeval x;
220  x.tv_sec = t.count() / 1000;
221  x.tv_usec = (t.count() % 1000) * 1000; // microseconds
222 
223  socklen_t len = sizeof(x);
224  if (::setsockopt(m_fd, SOL_SOCKET, SO_SNDTIMEO, &x, len))
225  {
226  std::stringstream st;
227  st << "setsockopt(SO_SNDTIMEO)";
228  throw std::system_error(errno, std::system_category(), st.str());
229  }
230 }
231 
232 void SocketBase::recv_timeout(std::chrono::milliseconds t)
233 {
234  timeval x;
235  x.tv_sec = t.count() / 1000;
236  x.tv_usec = (t.count() % 1000) * 1000; // microseconds
237 
238  socklen_t len = sizeof(x);
239  if (::setsockopt(m_fd, SOL_SOCKET, SO_RCVTIMEO, &x, len))
240  {
241  std::stringstream st;
242  st << "setsockopt(SO_RCVTIMEO)";
243  throw std::system_error(errno, std::system_category(), st.str());
244  }
245 }
246 
247 size_t SocketBase::recv(void* loc, size_t len, std::error_code& ec) noexcept
248 {
249  ec.clear();
250 
251  ssize_t sz;
252  do
253  {
254  sz = ::recv(m_fd, loc, len, 0);
255  }
256  while (sz == -1 && errno == EINTR);
257 
258  if (sz == -1)
259  {
260  ec.assign(errno, std::system_category());
261  sz = 0;
262  }
263 
264  return sz;
265 }
266 
267 size_t SocketBase::recv(void* loc, size_t len)
268 {
269  std::error_code ec;
270  size_t sz = recv(loc, len, ec);
271  if (ec.value())
272  {
273  std::stringstream st;
274  st << "recv()";
275  throw std::system_error(ec, st.str());
276  }
277  return sz;
278 }
279 
280 size_t SocketBase::send(const void* loc, size_t len, std::error_code& ec) noexcept
281 {
282  ec.clear();
283 
284  ssize_t sz;
285  do
286  {
287  sz = ::send(m_fd, loc, len, MSG_NOSIGNAL); // don't generate a SIGPIPE if the other end drops the connection
288  }
289  while (sz == -1 && errno == EINTR);
290 
291  if (sz == -1)
292  {
293  ec.assign(errno, std::system_category());
294  sz = 0;
295  }
296 
297  return sz;
298 }
299 
300 size_t SocketBase::send(const void* loc, size_t len)
301 {
302  std::error_code ec;
303  size_t sz = send(loc, len, ec);
304  if (ec.value())
305  {
306  std::stringstream st;
307  st << "send()";
308  throw std::system_error(ec, st.str());
309  }
310  return sz;
311 }
312 
314 {
315  if (::bind(m_fd, a, a.len()))
316  {
317  std::stringstream st;
318  st << "bind()";
319  throw std::system_error(errno, std::system_category(), st.str());
320  }
321 }
322 
323 void SocketBase::get_sockaddr(sockaddr& a)
324 {
325  socklen_t len = sizeof(a);
326  if (::getsockname(m_fd, &a, &len))
327  {
328  std::stringstream st;
329  st << "getsockname()";
330  throw std::system_error(errno, std::system_category(), st.str());
331  }
332 }
333 
334 void TcpSocket::listen(int max_connection_backlog)
335 {
336  if (::listen(fd(), max_connection_backlog))
337  {
338  std::stringstream st;
339  st << "listen()";
340  throw std::system_error(errno, std::system_category(), st.str());
341  }
342 }
343 
344 int TcpSocket::accept(sockaddr* peer, int len, std::error_code& ec) noexcept
345 {
346  ec.clear();
347 
348  socklen_t sl;
349  sockaddr* sa = nullptr;
350  socklen_t* sp = nullptr;
351 
352  if (peer)
353  {
354  sl = len;
355  sa = peer;
356  sp = &sl;
357  }
358 
359  int nfd;
360  do
361  {
362  nfd = ::accept(fd(), sa, sp); // get our new file descriptor
363  }
364  while (nfd == -1 && errno == EINTR);
365 
366  if (nfd == -1)
367  {
368  ec.assign(errno, std::system_category());
369  }
370 
371  //assert(peer == nullptr || sl >= len); // the unix socket length is variable
372 
373  return nfd;
374 }
375 
376 void TcpSocket::connect(SockaddrBase& p, std::error_code& ec) noexcept
377 {
378  ec.clear();
379 
380  int ret;
381  // connect to partner
382  do
383  {
384  ret = ::connect(fd(), p, p.len());
385  }
386  while (ret == -1 && errno == EINTR);
387 
388  if (ret)
389  {
390  ec.assign(errno, std::system_category());
391  }
392 }
393 
398 {
399  std::error_code ec;
400  connect(p, ec);
401  if (ec.value())
402  {
403  std::stringstream st;
404  st << "connect()";
405  throw std::system_error(ec, st.str());
406  }
407 }
408 
416 {
417  if (::shutdown(fd(), SHUT_RDWR))
418  {
419  std::stringstream st;
420  st << "shutdown()";
421  throw std::system_error(errno, std::system_category(), st.str());
422  }
423 }
424 
425 UdpSocket::UdpSocket(int domain, int stype, int proto) : SocketBase(domain, stype, proto) { }
426 
427 size_t UdpSocket::recv(void* loc, size_t len, SockaddrBase& a, std::error_code& ec) noexcept
428 {
429  ec.clear();
430 
431  socklen_t alen = a.len();
432 
433  ssize_t sz;
434  do
435  {
436  sz = ::recvfrom(fd(), loc, len, 0, a, &alen);
437  }
438  while (sz == -1 && errno == EINTR);
439 
440  if (sz == -1)
441  {
442  ec.assign(errno, std::system_category());
443  sz = 0;
444  }
445  else
446  {
447  assert(alen <= a.len()); // in the case of unix domain sockets can return smaller length
448  }
449 
450  return sz;
451 }
452 
453 size_t UdpSocket::recv(void* loc, size_t len, SockaddrBase& a)
454 {
455  std::error_code ec;
456  size_t sz = recv(loc, len, a, ec);
457  if (ec.value())
458  {
459  std::stringstream st;
460  st << "recv()";
461  throw std::system_error(ec, st.str());
462  }
463  return sz;
464 }
465 
466 size_t UdpSocket::send(const void* loc, size_t len, const SockaddrBase& a, std::error_code& ec) noexcept
467 {
468  ec.clear();
469 
470  ssize_t sz;
471  do
472  {
473  sz = ::sendto(fd(), loc, len, 0, a, a.len());
474  }
475  while (sz == -1 && errno == EINTR);
476 
477  if (sz == -1)
478  {
479  ec.assign(errno, std::system_category());
480  sz = 0;
481  }
482 
483  return sz;
484 }
485 
486 size_t UdpSocket::send(const void* loc, size_t len, const SockaddrBase& a)
487 {
488  std::error_code ec;
489  size_t sz = send(loc, len, a, ec);
490  if (ec.value())
491  {
492  std::stringstream st;
493  st << "send()";
494  throw std::system_error(ec, st.str());
495  }
496  return sz;
497 }
498 
500 {
501  int v, r;
502  do
503  {
504  r = ::ioctl(fd(), FIONREAD, &v);
505  }
506  while (r == -1 && errno == EINTR);
507  if (r == -1)
508  {
509  std::stringstream st;
510  st << "ioctl(FIONREAD)";
511  throw std::system_error(errno, std::system_category(), st.str());
512  }
513  return v;
514 }
Socket address base class.
Definition: socket.h:60
Socket base class.
Definition: socket.h:77
int fd() const
Return the underlying socket handle.
Definition: socket.h:112
unsigned send_bufsize()
Get total send buffer size including overhead.
Definition: socket.cc:148
std::error_code error_code()
Get the current error code (status).
Definition: socket.cc:94
void recv_timeout(std::chrono::milliseconds)
Set the receive timeout.
Definition: socket.cc:232
size_t recv(void *loc, size_t len)
Receive bytes, throwing an exception on error.
Definition: socket.cc:267
void send_timeout(std::chrono::milliseconds)
Set the send timeout.
Definition: socket.cc:217
void non_blocking(bool b=true)
Set the socket non-blocking.
Definition: socket.cc:188
size_t send(const void *loc, size_t len)
Send bytes, throwing an exception on error.
Definition: socket.cc:300
void bind(const SockaddrBase &)
Bind an address to the socket.
Definition: socket.cc:313
void reuse_addr(bool r=true)
Set address reusable.
Definition: socket.cc:162
unsigned recv_bufsize()
Get total receive buffer size including overhead.
Definition: socket.cc:121
void reuse_port(bool r=true)
Set port reusable.
Definition: socket.cc:175
void close()
Close the socket.
Definition: socket.cc:72
void shutdown()
Shutdown the connection; no further reads or writes will be allowed.
Definition: socket.cc:415
void listen(int maxConnections=10)
Set to accept connections.
Definition: socket.cc:334
int accept(sockaddr *, int len, std::error_code &) noexcept
Accept a connection.
Definition: socket.cc:344
void connect(SockaddrBase &)
Connect to a socket address.
Definition: socket.cc:397
size_t recv(void *loc, size_t len, SockaddrBase &s)
Receive bytes (a datagram), setting the socket address of the peer.
Definition: socket.cc:453
size_t send(const void *loc, size_t len, const SockaddrBase &d)
Send bytes (a datagram) to a peer address.
Definition: socket.cc:486
size_t recv_next()
Return the number of bytes available to read (the size of the next datagram).
Definition: socket.cc:499
int safe_close(int fd)
Signal safe close.
Definition: safe_clib.cc:95
Signal-safe C library wrapper.
std::ostream & operator<<(std::ostream &os, const SockaddrBase &sa)
Helper to print socket address to output stream.
Definition: socket.cc:51
Low-level tcp and udp sockets.