scclib
Stable Cloud Computing C++ Library
inet.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/inet.h>
32 #include <net/socket.h>
33 #include <cerrno>
34 #include <stdexcept>
35 #include <system_error>
36 #include <string>
37 #include <sstream>
38 #include <sys/ioctl.h>
39 #include <arpa/inet.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <cstring>
43 
49 using namespace scc::net;
50 
51 void InetAddr::init()
52 {
53  m_addr->sin6_family = AF_INET6;
54  m_addr->sin6_port = 0;
55  m_addr->sin6_flowinfo = 0;
56  m_addr->sin6_addr = in6addr_any;
57  m_addr->sin6_scope_id = 0;
58 }
59 
60 InetAddr::InetAddr() : m_addr(new sockaddr_in6)
61 {
62  init();
63 }
64 
66 {
67  delete m_addr;
68 }
69 
71 {
72  port(p);
73 }
74 
75 InetAddr::InetAddr(const sockaddr* a) : InetAddr()
76 {
77  if (a->sa_family == AF_INET)
78  {
79  const sockaddr_in* x = reinterpret_cast<const sockaddr_in*>(a);
80  // convert to mapped IPv4 address
81  //uint32_t h = ntohl(x->sin_addr.s_addr);
82  uint32_t h = x->sin_addr.s_addr;
83  unsigned char* b = reinterpret_cast<unsigned char*>(&h);
84  m_addr->sin6_addr.s6_addr[10] = 0xff;
85  m_addr->sin6_addr.s6_addr[11] = 0xff;
86  m_addr->sin6_addr.s6_addr[12] = b[0];
87  m_addr->sin6_addr.s6_addr[13] = b[1];
88  m_addr->sin6_addr.s6_addr[14] = b[2];
89  m_addr->sin6_addr.s6_addr[15] = b[3];
90 
91  m_addr->sin6_port = x->sin_port;
92  }
93  else if (a->sa_family == AF_INET6)
94  {
95  memcpy(m_addr, a, sizeof(sockaddr_in6));
96  }
97 }
98 
100 {
101  memcpy(m_addr, b.m_addr, sizeof(sockaddr_in6));
102 }
103 
105 {
106  memcpy(m_addr, b.m_addr, sizeof(sockaddr_in6));
107  return *this;
108 }
109 
111 {
112  memcpy(m_addr, b.m_addr, sizeof(sockaddr_in6));
113  b.init();
114 }
115 
117 {
118  memcpy(m_addr, b.m_addr, sizeof(sockaddr_in6));
119  b.init();
120  return *this;
121 }
122 
123 InetAddr::InetAddr(const std::string& h, unsigned p) : InetAddr()
124 {
125  host(h);
126  port(p);
127 }
128 
129 InetAddr::operator const sockaddr*() const
130 {
131  return reinterpret_cast<const sockaddr*>(m_addr);
132 }
133 
134 InetAddr::operator sockaddr*()
135 {
136  return reinterpret_cast<sockaddr*>(m_addr);
137 }
138 
139 unsigned InetAddr::len() const
140 {
141  return sizeof(sockaddr_in6);
142 }
143 
145 {
146  m_addr->sin6_addr = in6addr_any;
147 }
148 
150 {
151  m_addr->sin6_addr = in6addr_loopback;
152 }
153 
154 void InetAddr::port(unsigned p)
155 {
156  m_addr->sin6_port = htons(p);
157 }
158 
159 unsigned InetAddr::port() const
160 {
161  return ntohs(m_addr->sin6_port);
162 }
163 
164 
165 void InetAddr::scope_id(uint32_t s)
166 {
167  m_addr->sin6_scope_id = s;
168 }
169 
170 uint32_t InetAddr::scope_id() const
171 {
172  return m_addr->sin6_scope_id;
173 }
174 
175 void InetAddr::host(const std::string& h)
176 {
177  in6_addr a;
178 
179  if (inet_pton(AF_INET6, h.c_str(), &a) <= 0) { // set the address
180  std::ostringstream os;
181  os << "InetAddr::host('" << h << "'): invalid address";
182  throw std::runtime_error(os.str());
183  }
184 
185  m_addr->sin6_addr = a;
186 }
187 
188 std::string InetAddr::host() const
189 {
190  char buf[INET6_ADDRSTRLEN];
191 
192  if (inet_ntop(AF_INET6, &m_addr->sin6_addr, buf, INET6_ADDRSTRLEN) == nullptr) // copy address to string
193  {
194  std::stringstream st;
195  st << "inet_ntop()";
196  throw std::system_error(errno, std::system_category(), st.str());
197  }
198 
199  return std::string(buf);
200 }
201 
202 // test len bytes to see if they are equal to t
203 static bool btest(const unsigned char* b, unsigned t, int len)
204 {
205  for (; len > 0; len--, b++)
206  {
207  if (*b != t)
208  {
209  return false;
210  }
211  }
212  return true;
213 }
214 
215 int InetAddr::flags() const
216 {
217  int flags = 0;
218 
219  const unsigned char* ad = m_addr->sin6_addr.s6_addr;
220  if (btest(ad, 0, 10) && ad[10] == 0xff && ad[11] == 0xff)
221  {
223 
224  if (btest(ad+12, 0, 4))
225  {
227  }
228  else if (ad[12] == 127)
229  {
231  }
232  else if (ad[12] >= 224 && ad[12] <= 239)
233  {
236  }
237  else
238  {
241  }
242  }
243  else
244  {
246 
247  if (btest(ad, 0, 16)) // ::
248  {
250  }
251  else if (btest(ad, 0, 15) && ad[15] == 1) // ::1
252  {
254  }
255  else if (ad[0] == 0xff)
256  {
258  // flags
259  if ((ad[1] & 0x10) == 0x10)
260  {
262  }
263  if ((ad[1] & 0x20) == 0x10)
264  {
266  }
267  if ((ad[1] & 0x40) == 0x10)
268  {
270  }
271  // scope
272  switch (ad[1] & 0xf)
273  {
274  case 0x1: flags |= InetAddrFlag::if_local; break;
275  case 0x2: flags |= InetAddrFlag::link_local; break;
276  case 0x3: flags |= InetAddrFlag::realm_local; break;
277  case 0x4: flags |= InetAddrFlag::admin_local; break;
278  case 0x5: flags |= InetAddrFlag::site_local; break;
279  case 0x8: flags |= InetAddrFlag::org_local; break;
280  case 0xe: flags |= InetAddrFlag::global; break;
281  }
282  // reserved addresses ff00::/24
283  if ((ad[1] & 0xf0) == 0x00)
284  {
285  if (btest(ad+2, 0, 13) && ad[15] == 1) // ff0X::1
286  {
288  }
289  if (btest(ad+2, 0, 13) && ad[15] == 2) // ff0X::2
290  {
292  }
293  }
294  }
295  else
296  {
298 
299  // fe80::/64
300  if (ad[0] == 0xfe && ad[1] == 0x80 && btest(ad+2, 0, 6)) // 8 bytes total
301  {
303  }
304  else
305  {
307  }
308 
309  // fc00::/7; currently fd00::/8 is defined for /48 prefixes
310  if ((ad[0] & 0xfe) == 0xfc)
311  {
313  }
314  }
315  }
316 
317  return flags;
318 }
319 
320 std::ostream& operator<<(std::ostream& os, const InetAddr& sa)
321 {
322  return os.write(sa.str().c_str(), sa.str().size());
323 }
324 
325 std::string InetAddr::str() const
326 {
327  std::stringstream s;
328  int f = flags();
329  switch (f & InetAddrFlag::prot_mask)
330  {
331  case InetAddrFlag::ipv4:
332  s << "ipv4";
333  break;
334  case InetAddrFlag::ipv6:
335  s << "ipv6";
336  break;
337  }
338  s << " " << host();
339  s << " port: " << port() << " scope_id: " << scope_id();
340  s << " flags:";
341  switch (f & InetAddrFlag::type_mask)
342  {
343  case InetAddrFlag::any:
344  s << " type-any";
345  break;
347  s << " type-loop";
348  break;
350  s << " type-mcast";
351  break;
353  s << " type-unicast";
354  break;
355  }
356 
357  switch (f & scope_mask)
358  {
360  s << " scope-iface-local";
361  break;
363  s << " scope-link-local";
364  break;
366  s << " scope-realm-local";
367  break;
369  s << " scope-admin-local";
370  break;
372  s << " scope-site-local";
373  break;
375  s << " scope-org-local";
376  break;
378  s << " scope-global";
379  break;
380  }
381 
382  switch (f & InetAddrFlag::mcast_flags_mask)
383  {
385  s << " mcast-flags-rendezvous";
386  break;
388  s << " mcast-flags-prefix";
389  break;
391  s << " mcast-flags-dynamic";
392  break;
393  }
394 
396  {
398  s << " mcast-all-nodes";
399  break;
401  s << " mcast-all-routers";
402  break;
403  }
404 
406  {
408  s << " unique-local-address";
409  break;
410  }
411 
412  return s.str();
413 }
414 
415 InetTcpSock::InetTcpSock() : TcpSocket(AF_INET6, SOCK_STREAM, 0)
416 {}
417 
419 {
420  SocketBase::reset(AF_INET6, SOCK_STREAM, 0);
421 }
422 
424 {
425  sockaddr a;
426  get_sockaddr(a);
427  return InetAddr(&a);
428 }
429 
431 {
432  std::error_code ec;
433  InetTcpSock s(TcpSocket::accept(nullptr, 0, ec));
434  if (ec.value())
435  {
436  std::stringstream st;
437  st << "accept()";
438  throw std::system_error(ec, st.str());
439  }
440  return s;
441 }
442 
444 {
445  std::error_code ec;
446  InetTcpSock s(TcpSocket::accept(peer, peer.len(), ec));
447  if (ec.value())
448  {
449  std::stringstream st;
450  st << "accept(peer)";
451  throw std::system_error(ec, st.str());
452  }
453  return s;
454 }
455 
456 std::shared_ptr<InetTcpSock> InetTcpSock::accept_shared()
457 {
458  std::error_code ec;
459  std::shared_ptr<InetTcpSock> s(new InetTcpSock(TcpSocket::accept(nullptr, 0, ec)));
460  if (ec.value())
461  {
462  std::stringstream st;
463  st << "accept()";
464  throw std::system_error(ec, st.str());
465  }
466  return s;
467 }
468 
469 std::shared_ptr<InetTcpSock> InetTcpSock::accept_shared(InetAddr& peer)
470 {
471  std::error_code ec;
472  std::shared_ptr<InetTcpSock> s(new InetTcpSock(TcpSocket::accept(peer, peer.len(), ec)));
473  if (ec.value())
474  {
475  std::stringstream st;
476  st << "accept(peer)";
477  throw std::system_error(ec, st.str());
478  }
479  return s;
480 }
481 
482 InetUdpSock::InetUdpSock() : UdpSocket(AF_INET6, SOCK_DGRAM, 0) { }
483 
485 {
486  SocketBase::reset(AF_INET6, SOCK_DGRAM, 0);
487 }
488 
490 {
491  sockaddr a;
492  get_sockaddr(a);
493  return InetAddr(&a);
494 }
495 
496 void InetUdpSock::mcast_join_group(const InetAddr& group_addr, unsigned interface)
497 {
498  const sockaddr_in6* a{group_addr};
499  ipv6_mreq mr;
500  mr.ipv6mr_multiaddr = a->sin6_addr;
501  mr.ipv6mr_interface = interface;
502 
503  socklen_t len = sizeof(mr);
504 
505  if (::setsockopt(fd(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, len))
506  {
507  std::stringstream st;
508  st << "mcast join group setsockopt()";
509  throw std::system_error(errno, std::system_category(), st.str());
510  }
511 }
512 
513 void InetUdpSock::mcast_leave_group(const InetAddr& group_addr, unsigned interface)
514 {
515  const sockaddr_in6* a{group_addr};
516  ipv6_mreq mr;
517  mr.ipv6mr_multiaddr = a->sin6_addr;
518  mr.ipv6mr_interface = interface;
519 
520  socklen_t len = sizeof(mr);
521 
522  if (::setsockopt(fd(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mr, len))
523  {
524  std::stringstream st;
525  st << "mcast leave group setsockopt()";
526  throw std::system_error(errno, std::system_category(), st.str());
527  }
528 }
529 
530 void InetUdpSock::mcast_interface(unsigned interface)
531 {
532  unsigned v{interface};
533  socklen_t len = sizeof(v);
534 
535  if (::setsockopt(fd(), IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, len))
536  {
537  std::stringstream st;
538  st << "mcast interface setsockopt()";
539  throw std::system_error(errno, std::system_category(), st.str());
540  }
541 }
542 
544 {
545  unsigned x = loop ? 1 : 0;
546  socklen_t len = sizeof(x);
547 
548  if (::setsockopt(fd(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &x, len))
549  {
550  std::stringstream st;
551  st << "mcast loopback setsockopt()";
552  throw std::system_error(errno, std::system_category(), st.str());
553  }
554 }
555 
556 void InetUdpSock::mcast_hops(unsigned hops)
557 {
558  unsigned v{hops};
559  socklen_t len = sizeof(v);
560 
561  if (::setsockopt(fd(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &v, len))
562  {
563  std::stringstream st;
564  st << "mcast hops setsockopt()";
565  throw std::system_error(errno, std::system_category(), st.str());
566  }
567 }
Ipv6 internet address.
Definition: inet.h:120
virtual unsigned len() const
Socket address length in bytes.
Definition: inet.cc:139
unsigned port() const
Get the port.
Definition: inet.cc:159
InetAddr & operator=(const InetAddr &)
ipv6 internet address, copy assigned.
Definition: inet.cc:104
uint32_t scope_id() const
Get the scope id of the address.
Definition: inet.cc:170
void any_host()
Set to "any" host address ::
Definition: inet.cc:144
void local_host()
Set to local (loopback) address ::1.
Definition: inet.cc:149
virtual std::string host() const
Get host.
Definition: inet.cc:188
virtual ~InetAddr()
ipv6 internet address destructor.
Definition: inet.cc:65
virtual std::string str() const
Readable address string.
Definition: inet.cc:325
InetAddr()
ipv6 internet address, initialized with "any" address
Definition: inet.cc:60
int flags() const
Return the address flags.
Definition: inet.cc:215
Internet transmission control protocol (tcp) socket.
Definition: inet.h:251
virtual void reset()
Close the connection and reset the socket.
Definition: inet.cc:418
InetTcpSock accept()
Accept a connection from an anonymous peer.
Definition: inet.cc:430
InetTcpSock()
Create an IPv6 stream socket.
Definition: inet.cc:415
InetAddr get_addr()
Get the socket address.
Definition: inet.cc:423
virtual void reset()
Reset the socket.
Definition: inet.cc:484
InetUdpSock()
Create an IPv6 datagram socket.
Definition: inet.cc:482
void mcast_leave_group(const InetAddr &, unsigned=0)
Leave a multicast group.
Definition: inet.cc:513
InetAddr get_addr()
Get the socket address.
Definition: inet.cc:489
void mcast_interface(unsigned=0)
Set the default interface for outgoing multicast messages.
Definition: inet.cc:530
void mcast_loopback(bool=true)
Enable or disable multicast loopback.
Definition: inet.cc:543
void mcast_hops(unsigned=1)
Hop limit for outgoing multicast messages.
Definition: inet.cc:556
void mcast_join_group(const InetAddr &, unsigned=0)
Join a multicast group.
Definition: inet.cc:496
int fd() const
Return the underlying socket handle.
Definition: socket.h:112
Tcp socket base class.
Definition: socket.h:291
int accept(sockaddr *, int len, std::error_code &) noexcept
Accept a connection.
Definition: socket.cc:344
Udp socket base class.
Definition: socket.h:349
@ unicast
Unicast address.
Definition: inet.h:82
@ link_local
Traffic is restricted to the local link.
Definition: inet.h:87
@ realm_local
Traffic is restricted to the local realm.
Definition: inet.h:88
@ scope_mask
Scope for multicast addresses mask.
Definition: inet.h:85
@ mcast_all_nodes
Reaches all nodes in the scope, e.g. ff0X::1.
Definition: inet.h:100
@ mcast_rendezvous
Address has a rendezvous point embedded.
Definition: inet.h:95
@ mcast_flags_mask
Multicast flags mask.
Definition: inet.h:94
@ admin_local
Traffic is restricted to the local admin.
Definition: inet.h:89
@ unicast_special_mask
Some special unicast addresses.
Definition: inet.h:103
@ global
Global traffic is allowed.
Definition: inet.h:92
@ org_local
Traffic is restricted to the local organization.
Definition: inet.h:91
@ mcast_all_routers
Reaches all routers in the scope, e.g. ff0X::2.
Definition: inet.h:101
@ any
Any address.
Definition: inet.h:80
@ loopback
Loopback address.
Definition: inet.h:81
@ prot_mask
Protocol mask.
Definition: inet.h:75
@ if_local
Traffic is restricted to the local interface.
Definition: inet.h:86
@ type_mask
Address mask.
Definition: inet.h:79
@ ipv4
IPv4.
Definition: inet.h:76
@ mcast_reserved_mask
Some reserved multicast addresses.
Definition: inet.h:99
@ mcast_dynamic
Dynamic (temporary) address, otherwise permanent (assigned).
Definition: inet.h:97
@ ipv6
IPv6.
Definition: inet.h:77
@ site_local
Traffic is restricted to the local site.
Definition: inet.h:90
@ multicast
Multicast address.
Definition: inet.h:83
@ unique_local_address
Address which can be used freely within a site: e.g. fd00::/8.
Definition: inet.h:104
@ mcast_prefix
Prefix-based address.
Definition: inet.h:96
std::ostream & operator<<(std::ostream &os, const InetAddr &sa)
Print the socket address details to an output stream.
Definition: inet.cc:320
Internet tcp and udp networking.
Low-level tcp and udp sockets.