E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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