VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gaddress6.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gaddress6.cpp
19 //
20 // This file is formatted for side-by-side comparison with gaddress4.cpp.
21 
22 #include "gdef.h"
23 #include "gnet.h"
24 #include "gaddress6.h"
25 #include "gstrings.h"
26 #include "gstr.h"
27 #include "gassert.h"
28 #include "gdebug.h"
29 #include <utility> // std::swap()
30 #include <algorithm> // std::swap()
31 #include <climits>
32 #include <sys/types.h>
33 #include <sstream>
34 #include <vector>
35 #include <iomanip>
36 
37 namespace
38 {
39  const char * port_separators = ":/." ;
40  char port_separator = '.' ;
41 }
42 
43 unsigned short GNet::Address6::family()
44 {
45  return AF_INET6 ;
46 }
47 
48 int GNet::Address6::domain()
49 {
50  return PF_INET6 ;
51 }
52 
53 void GNet::Address6::init()
54 {
55  static specific_type zero ;
56  m_inet.specific = zero ;
57  m_inet.specific.sin6_family = family() ;
58  m_inet.specific.sin6_flowinfo = 0 ;
59  m_inet.specific.sin6_port = 0 ;
60  gnet_address6_init( m_inet.specific ) ; // gdef.h
61 }
62 
63 GNet::Address6::Address6( unsigned int port )
64 {
65  init() ;
66  m_inet.specific.sin6_addr = in6addr_any ;
67  const char * reason = setPort( m_inet , port ) ;
68  if( reason ) throw Address::Error(reason) ;
69 }
70 
71 GNet::Address6::Address6( unsigned int port , int )
72 {
73  init() ;
74  m_inet.specific.sin6_addr = in6addr_loopback ;
75  const char * reason = setPort( m_inet , port ) ;
76  if( reason ) throw Address::Error(reason) ;
77 }
78 
79 GNet::Address6::Address6( const sockaddr * addr , socklen_t len )
80 {
81  init() ;
82  if( addr == nullptr )
83  throw Address::Error() ;
84  if( addr->sa_family != family() || static_cast<size_t>(len) < sizeof(specific_type) )
85  throw Address::BadFamily() ;
86 
87  m_inet.specific = *(reinterpret_cast<const specific_type*>(addr)) ;
88 }
89 
90 GNet::Address6::Address6( const Address6 & other )
91 {
92  m_inet.specific = other.m_inet.specific ;
93 }
94 
95 GNet::Address6::Address6( const std::string & host_part , unsigned int port )
96 {
97  init() ;
98  const char * reason = setHostAddress( m_inet , host_part ) ;
99  if( !reason )
100  reason = setPort( m_inet , port ) ;
101  if( reason )
102  throw Address::BadString( std::string(reason) + ": " + host_part ) ;
103 }
104 
105 GNet::Address6::Address6( const std::string & host_part , const std::string & port_part )
106 {
107  init() ;
108  const char * reason = setHostAddress( m_inet , host_part ) ;
109  if( !reason )
110  reason = setPort( m_inet , port_part ) ;
111  if( reason )
112  throw Address::BadString( std::string(reason) + ": [" + host_part + "][" + port_part + "]" ) ;
113 }
114 
115 GNet::Address6::Address6( const std::string & display_string )
116 {
117  init() ;
118  const char * reason = setAddress( m_inet , display_string ) ;
119  if( reason )
120  throw Address::BadString( std::string(reason) + ": " + display_string ) ;
121 }
122 
123 const char * GNet::Address6::setAddress( union_type & inet , const std::string & display_string )
124 {
125  const std::string::size_type pos = display_string.find_last_of( port_separators ) ;
126  if( pos == std::string::npos )
127  return "no port separator" ;
128 
129  std::string host_part = G::Str::head( display_string , pos ) ;
130  std::string port_part = G::Str::tail( display_string , pos ) ;
131 
132  const char * reason = setHostAddress( inet , host_part ) ;
133  if( !reason )
134  reason = setPort( inet , port_part ) ;
135  return reason ;
136 }
137 
138 const char * GNet::Address6::setHostAddress( union_type & inet , const std::string & host_part )
139 {
140  // wikipedia: "because all link-local addresses in a host have a common prefix, normal routing
141  // procedures cannot be used to choose the outgoing interface when sending packets
142  // to a link-local destination -- a special identifier, known as a zone index, is needed
143  // to provide the additional routing information -- in the case of link-local addresses
144  // zone indices correspond to interface identifiers -- when an address is written
145  // textually the zone index is appended to the address separated by a percent sign --
146  // the actual syntax of zone indices depends on the operating system"
147  //
148  // see also rfc2553 section 4
149  //
150  std::string zone = G::Str::tail( host_part , host_part.find('%') , std::string() ) ;
151  std::string host_part_head = G::Str::head( host_part , host_part.find('%') , host_part ) ;
152 
153  int rc = inet_pton( family() , host_part_head.c_str() , &inet.specific.sin6_addr ) ;
154 
155  if( rc == 1 && !zone.empty() )
156  {
157  unsigned int zone_id = G::Str::toUInt( zone , "0" ) ;
158  if( zone_id == 0U )
159  zone_id = if_nametoindex( zone.c_str() ) ;
160  if( zone_id != 0U )
161  setZone( inet , zone_id ) ; // sin6_scope_id
162  }
163 
164  return rc == 1 ? nullptr : "invalid network address" ;
165 }
166 
167 void GNet::Address6::setPort( unsigned int port )
168 {
169  const char * reason = setPort( m_inet , port ) ;
170  if( reason )
171  throw Address::Error( "invalid port number" ) ;
172 }
173 
174 const char * GNet::Address6::setPort( union_type & inet , const std::string & port_part )
175 {
176  if( port_part.length() == 0U ) return "empty port string" ;
177  if( !G::Str::isNumeric(port_part) || !G::Str::isUInt(port_part) ) return "non-numeric port string" ;
178  return setPort( inet , G::Str::toUInt(port_part) ) ;
179 }
180 
181 const char * GNet::Address6::setPort( union_type & inet , unsigned int port )
182 {
183  if( port > 0xFFFFU ) return "port number too big" ;
184  const g_port_t in_port = static_cast<g_port_t>(port) ;
185  inet.specific.sin6_port = htons( in_port ) ;
186  return nullptr ;
187 }
188 
189 void GNet::Address6::setZone( union_type & inet , unsigned int zone_id )
190 {
191  inet.specific.sin6_scope_id = zone_id ;
192  //inet.specific.sin6_scope_struct.Level = 0 ;
193  //inet.specific.sin6_scope_struct.Zone = zone_id ;
194 }
195 
196 std::string GNet::Address6::displayString() const
197 {
198  const bool with_scope_id = false ;
199  std::ostringstream ss ;
200  ss << hostPartString() ;
201  if( with_scope_id )
202  ss << "%" << scopeId() ;
203  ss << port_separator << port() ;
204  return ss.str() ;
205 }
206 
207 std::string GNet::Address6::hostPartString() const
208 {
209  char buffer[INET6_ADDRSTRLEN+1U] ;
210  const void * vp = & m_inet.specific.sin6_addr ;
211  const char * p = inet_ntop( family() , const_cast<void*>(vp) , buffer , sizeof(buffer) ) ; // (const cast for windows)
212  if( p == nullptr )
213  throw Address::Error( "inet_ntop() failure" ) ;
214  buffer[sizeof(buffer)-1U] = '\0' ;
215  return std::string(buffer) ;
216 }
217 
218 bool GNet::Address6::validData( const sockaddr * addr , socklen_t len )
219 {
220  return addr != nullptr && addr->sa_family == family() && len == sizeof(specific_type) ;
221 }
222 
223 bool GNet::Address6::validString( const std::string & s , std::string * reason_p )
224 {
225  union_type inet ;
226  const char * reason = setAddress( inet , s ) ;
227  if( reason && reason_p )
228  *reason_p = std::string(reason) ;
229  return reason == nullptr ;
230 }
231 
232 bool GNet::Address6::validStrings( const std::string & host_part , const std::string & port_part , std::string * reason_p )
233 {
234  union_type inet ;
235  const char * reason = setHostAddress( inet , host_part ) ;
236  if( !reason )
237  reason = setPort( inet , port_part ) ;
238  if( reason && reason_p )
239  *reason_p = std::string(reason) ;
240  return reason == nullptr ;
241 }
242 
243 bool GNet::Address6::validPort( unsigned int port )
244 {
245  union_type inet ;
246  const char * reason = setPort( inet , port ) ;
247  return reason == nullptr ;
248 }
249 
250 bool GNet::Address6::same( const Address6 & other ) const
251 {
252  return
253  m_inet.specific.sin6_family == family() &&
254  other.m_inet.specific.sin6_family == family() &&
255  sameAddr( m_inet.specific.sin6_addr , other.m_inet.specific.sin6_addr ) &&
256  m_inet.specific.sin6_port == other.m_inet.specific.sin6_port ;
257 }
258 
259 bool GNet::Address6::sameHostPart( const Address6 & other ) const
260 {
261  return
262  m_inet.specific.sin6_family == family() &&
263  other.m_inet.specific.sin6_family == family() &&
264  sameAddr( m_inet.specific.sin6_addr , other.m_inet.specific.sin6_addr ) ;
265 }
266 
267 bool GNet::Address6::sameAddr( const ::in6_addr & a , const ::in6_addr & b )
268 {
269  for( size_t i = 0 ; i < 16U ; i++ )
270  {
271  if( a.s6_addr[i] != b.s6_addr[i] )
272  return false ;
273  }
274  return true ;
275 }
276 
277 unsigned int GNet::Address6::port() const
278 {
279  return ntohs( m_inet.specific.sin6_port ) ;
280 }
281 
282 unsigned long GNet::Address6::scopeId() const
283 {
284  return m_inet.specific.sin6_scope_id ;
285 }
286 
287 const sockaddr * GNet::Address6::address() const
288 {
289  return &m_inet.general ;
290 }
291 
292 sockaddr * GNet::Address6::address()
293 {
294  return &m_inet.general ;
295 }
296 
297 socklen_t GNet::Address6::length()
298 {
299  return sizeof(specific_type) ;
300 }
301 
302 namespace
303 {
304  void shiftLeft( struct in6_addr & mask )
305  {
306  bool carry_in = false ;
307  for( int i = 15 ; i >= 0 ; i-- )
308  {
309  const unsigned char top_bit = 128U ;
310  bool carry_out = !!( mask.s6_addr[i] & top_bit ) ;
311  mask.s6_addr[i] <<= 1U ;
312  if( carry_in ) ( mask.s6_addr[i] |= 1U ) ;
313  carry_in = carry_out ;
314  }
315  }
316  void shiftLeft( struct in6_addr & mask , unsigned int bits )
317  {
318  for( unsigned int i = 0U ; i < bits ; i++ )
319  shiftLeft( mask ) ;
320  }
321  void reset( struct in6_addr & addr )
322  {
323  for( unsigned int i = 0 ; i < 16U ; i++ )
324  addr.s6_addr[i] = 0 ;
325  }
326  void fill( struct in6_addr & addr )
327  {
328  for( unsigned int i = 0 ; i < 16U ; i++ )
329  addr.s6_addr[i] = 0xff ;
330  }
331  struct in6_addr make( unsigned int lhs_hi , unsigned int lhs_lo , unsigned int rhs )
332  {
333  struct in6_addr addr ;
334  reset( addr ) ;
335  addr.s6_addr[15] = rhs ;
336  addr.s6_addr[0] = lhs_hi ;
337  addr.s6_addr[1] = lhs_lo ;
338  return addr ;
339  }
340  void applyMask( struct in6_addr & addr , const struct in6_addr & mask )
341  {
342  for( int i = 0 ; i < 16 ; i++ )
343  {
344  addr.s6_addr[i] &= mask.s6_addr[i] ;
345  }
346  }
347  struct in6_addr mask( unsigned int bits )
348  {
349  struct in6_addr addr ;
350  fill( addr ) ;
351  shiftLeft( addr , 128U - bits ) ;
352  return addr ;
353  }
354  struct in6_addr masked( const struct in6_addr & addr_in , const struct in6_addr & mask )
355  {
356  struct in6_addr result = addr_in ;
357  applyMask( result , mask ) ;
358  return result ;
359  }
360 }
361 
362 G::StringArray GNet::Address6::wildcards() const
363 {
364  Address6 a( *this ) ;
365 
366  G::StringArray result ;
367  result.reserve( 128U ) ;
368  result.push_back( hostPartString() ) ;
369 
370  struct in6_addr mask ;
371  fill( mask ) ;
372 
373  for( int bit = 0 ; bit < 128 ; bit++ )
374  {
375  std::ostringstream ss ;
376  ss << a.hostPartString() << "/" << (128-bit) ;
377  result.push_back( ss.str() ) ;
378 
379  shiftLeft( mask ) ;
380  applyMask( a.m_inet.specific.sin6_addr , mask ) ;
381  }
382  return result ;
383 }
384 
385 bool GNet::Address6::isLoopback() const
386 {
387  // ::1/128
388  struct in6_addr _1 = make( 0U , 0U , 1U ) ;
389  return sameAddr( _1 , m_inet.specific.sin6_addr ) ;
390 }
391 
392 bool GNet::Address6::isLocal( std::string & reason ) const
393 {
394  struct in6_addr addr_128 = masked( m_inet.specific.sin6_addr , mask(128U) ) ; // degenerate mask
395  struct in6_addr addr_64 = masked( m_inet.specific.sin6_addr , mask(64U) ) ;
396  struct in6_addr addr_7 = masked( m_inet.specific.sin6_addr , mask(7U) ) ;
397 
398  struct in6_addr _1 = make( 0U , 0U , 1U ) ;
399  struct in6_addr _fe80 = make( 0xfeU , 0x80U , 0U ) ;
400  struct in6_addr _fc00 = make( 0xfcU , 0U , 0U ) ;
401 
402  bool local =
403  sameAddr( _1 , addr_128 ) ||
404  sameAddr( _fe80 , addr_64 ) ||
405  sameAddr( _fc00 , addr_7 ) ;
406 
407  if( !local )
408  {
409  std::ostringstream ss ;
410  ss << hostPartString() << " is not ::1/128 or in fe80::/64 or fc00::/7" ;
411  reason = ss.str() ;
412  }
413  return local ;
414 }
415 
416 /// \file gaddress6.cpp
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
Definition: gstr.cpp:228
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1051
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
static std::string head(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1037
This file is formatted for side-by-side comparison with gaddress4.h.
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
Definition: gstr.cpp:266