E-MailRelay

gaddress4.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file gaddress4.cpp
19///
20
21#include "gdef.h"
22#include "gaddress4.h"
23#include "gstr.h"
24#include "gtest.h"
25#include "gassert.h"
26#include "glog.h"
27#include <algorithm> // std::swap()
28#include <utility> // std::swap()
29#include <climits>
30#include <sys/types.h>
31#include <sstream>
32#include <array>
33
34 namespace GNet
35{
36 namespace Address4Imp
37 {
38 constexpr const char * port_separators = ":" ;
39 constexpr char port_separator = ':' ;
40 }
41}
42
43 unsigned short GNet::Address4::af() noexcept
44{
45 return AF_INET ;
46}
47
48 int GNet::Address4::domain() noexcept
49{
50 return PF_INET ;
51}
52
53GNet::Address4::Address4( std::nullptr_t ) :
54 m_inet{}
55{
56 m_inet.sin_family = af() ;
57 m_inet.sin_port = 0 ;
58}
59
60GNet::Address4::Address4( unsigned int port ) :
61 Address4(nullptr)
62{
63 m_inet.sin_addr.s_addr = htonl(INADDR_ANY);
64 const char * reason = setPort( m_inet , port ) ;
65 if( reason ) throw Address::Error(reason) ;
66}
67
68GNet::Address4::Address4( unsigned int port , int /*loopback_overload*/ ) :
69 Address4(nullptr)
70{
71 m_inet.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
72 const char * reason = setPort( m_inet , port ) ;
73 if( reason ) throw Address::Error(reason) ;
74}
75
76GNet::Address4::Address4( const sockaddr * addr , socklen_t len , bool ) :
77 Address4(nullptr)
78{
79 if( addr == nullptr )
80 throw Address::Error() ;
81 if( addr->sa_family != af() || static_cast<std::size_t>(len) < sizeof(sockaddr_type) )
82 throw Address::BadFamily() ;
83
84 m_inet = *(reinterpret_cast<const sockaddr_type*>(addr)) ;
85}
86
87GNet::Address4::Address4( const std::string & host_part , unsigned int port ) :
88 Address4(nullptr)
89{
90 const char * reason = setHostAddress( m_inet , host_part ) ;
91 if( !reason )
92 reason = setPort( m_inet , port ) ;
93 if( reason )
94 throw Address::BadString( std::string(reason) + ": " + host_part ) ;
95}
96
97GNet::Address4::Address4( const std::string & host_part , const std::string & port_part ) :
98 Address4(nullptr)
99{
100 const char * reason = setHostAddress( m_inet , host_part ) ;
101 if( !reason )
102 reason = setPort( m_inet , port_part ) ;
103 if( reason )
104 throw Address::BadString( std::string(reason) + ": [" + host_part + "][" + port_part + "]" ) ;
105}
106
107GNet::Address4::Address4( const std::string & display_string ) :
108 Address4(nullptr)
109{
110 const char * reason = setAddress( m_inet , display_string ) ;
111 if( reason )
112 throw Address::BadString( std::string(reason) + ": " + display_string ) ;
113}
114
115 const char * GNet::Address4::setAddress( sockaddr_type & inet , const std::string & display_string )
116{
117 const std::string::size_type pos = display_string.find_last_of( Address4Imp::port_separators ) ;
118 if( pos == std::string::npos )
119 return "no port separator" ;
120
121 std::string host_part = G::Str::head( display_string , pos ) ;
122 std::string port_part = G::Str::tail( display_string , pos ) ;
123
124 const char * reason = setHostAddress( inet , host_part ) ;
125 if( !reason )
126 reason = setPort( inet , port_part ) ;
127 return reason ;
128}
129
130 const char * GNet::Address4::setHostAddress( sockaddr_type & inet , const std::string & host_part )
131{
132 // start with a stricter check than inet_pton(), inet_addr() etc. since they allow eg. "123.123"
133 if( !Address4::format(host_part) )
134 return "invalid network address" ;
135
136 int rc = inet_pton( af() , host_part.c_str() , &inet.sin_addr ) ;
137 return rc == 1 ? nullptr : "invalid network address" ;
138}
139
140 void GNet::Address4::setPort( unsigned int port )
141{
142 const char * reason = setPort( m_inet , port ) ;
143 if( reason )
144 throw Address::Error( "invalid port number" ) ;
145}
146
147 const char * GNet::Address4::setPort( sockaddr_type & inet , const std::string & port_part )
148{
149 if( port_part.length() == 0U ) return "empty port string" ;
150 if( !G::Str::isNumeric(port_part) || !G::Str::isUInt(port_part) ) return "non-numeric port string" ;
151 return setPort( inet , G::Str::toUInt(port_part) ) ;
152}
153
154 const char * GNet::Address4::setPort( sockaddr_type & inet , unsigned int port )
155{
156 if( port > 0xFFFFU ) return "port number too big" ;
157 const g_port_t in_port = static_cast<g_port_t>(port) ;
158 inet.sin_port = htons( in_port ) ;
159 return nullptr ;
160}
161
162 bool GNet::Address4::setZone( const std::string & /*ipv6_zone_name_or_scope_id*/ )
163{
164 return true ;
165}
166
167 void GNet::Address4::setScopeId( unsigned long /*ipv6_scope_id*/ )
168{
169}
170
171std::string GNet::Address4::displayString( bool /*ipv6_with_scope*/ ) const
172{
173 std::ostringstream ss ;
174 ss << hostPartString() ;
175 ss << Address4Imp::port_separator << port() ;
176 return ss.str() ;
177}
178
179std::string GNet::Address4::hostPartString( bool /*raw*/ ) const
180{
181 std::array<char,INET_ADDRSTRLEN+1U> buffer {} ;
182 const void * vp = & m_inet.sin_addr ;
183 const char * p = inet_ntop( af() , const_cast<void*>(vp) , &buffer[0] , buffer.size() ) ;
184 if( p == nullptr )
185 throw Address::Error( "inet_ntop() failure" ) ;
186 buffer[buffer.size()-1U] = '\0' ;
187 return { &buffer[0] } ; // sic
188}
189
190std::string GNet::Address4::queryString() const
191{
192 G::StringArray parts = G::Str::splitIntoFields( hostPartString() , "." ) ;
193 std::reverse( parts.begin() , parts.end() ) ;
194 return G::Str::join( "." , parts ) ;
195}
196
197 bool GNet::Address4::validData( const sockaddr * addr , socklen_t len )
198{
199 return addr != nullptr && addr->sa_family == af() && len == sizeof(sockaddr_type) ;
200}
201
202 bool GNet::Address4::validString( const std::string & s , std::string * reason_p )
203{
204 sockaddr_type inet {} ;
205 const char * reason = setAddress( inet , s ) ;
206 if( reason && reason_p )
207 *reason_p = std::string(reason) ;
208 return reason == nullptr ;
209}
210
211 bool GNet::Address4::validStrings( const std::string & host_part , const std::string & port_part ,
212 std::string * reason_p )
213{
214 sockaddr_type inet {} ;
215 const char * reason = setHostAddress( inet , host_part ) ;
216 if( !reason )
217 reason = setPort( inet , port_part ) ;
218 if( reason && reason_p )
219 *reason_p = std::string(reason) ;
220 return reason == nullptr ;
221}
222
223 bool GNet::Address4::validPort( unsigned int port )
224{
225 sockaddr_type inet {} ;
226 const char * reason = setPort( inet , port ) ;
227 return reason == nullptr ;
228}
229
230 bool GNet::Address4::same( const Address4 & other , bool /*ipv6_compare_with_scope*/ ) const
231{
232 return
233 m_inet.sin_family == af() &&
234 other.m_inet.sin_family == af() &&
235 sameAddr( m_inet.sin_addr , other.m_inet.sin_addr ) &&
236 m_inet.sin_port == other.m_inet.sin_port ;
237}
238
239 bool GNet::Address4::sameHostPart( const Address4 & other ) const
240{
241 return
242 m_inet.sin_family == af() &&
243 other.m_inet.sin_family == af() &&
244 sameAddr( m_inet.sin_addr , other.m_inet.sin_addr ) ;
245}
246
247 bool GNet::Address4::sameAddr( const ::in_addr & a , const ::in_addr & b )
248{
249 return a.s_addr == b.s_addr ;
250}
251
252 unsigned int GNet::Address4::port() const
253{
254 return ntohs( m_inet.sin_port ) ;
255}
256
257 unsigned long GNet::Address4::scopeId( unsigned long default_ ) const
258{
259 return default_ ;
260}
261
262 const sockaddr * GNet::Address4::address() const
263{
264 return reinterpret_cast<const sockaddr*>(&m_inet) ;
265}
266
267sockaddr * GNet::Address4::address()
268{
269 return reinterpret_cast<sockaddr*>(&m_inet) ;
270}
271
272socklen_t GNet::Address4::length() noexcept
273{
274 return sizeof(sockaddr_type) ;
275}
276
277 G::StringArray GNet::Address4::wildcards() const
278{
279 std::string ip_string = hostPartString() ;
280
281 G::StringArray result ;
282 result.reserve( 38U ) ;
283 result.push_back( ip_string ) ;
284
285 G::StringArray part ;
286 part.reserve( 4U ) ;
287 G::Str::splitIntoFields( ip_string , part , "." ) ;
288
289 G_ASSERT( part.size() == 4U ) ;
290 if( part.size() != 4U )
291 return result ;
292
293 if( part[0].empty() || !G::Str::isUInt(part[0]) ||
294 part[1].empty() || !G::Str::isUInt(part[1]) ||
295 part[2].empty() || !G::Str::isUInt(part[2]) ||
296 part[3].empty() || !G::Str::isUInt(part[3]) )
297 {
298 return result ;
299 }
300
301 unsigned int n0 = G::Str::toUInt(part[0]) ;
302 unsigned int n1 = G::Str::toUInt(part[1]) ;
303 unsigned int n2 = G::Str::toUInt(part[2]) ;
304 unsigned int n3 = G::Str::toUInt(part[3]) ;
305
306 std::string part_0_1_2 = part[0] ;
307 part_0_1_2.append( 1U , '.' ) ;
308 part_0_1_2.append( part[1] ) ;
309 part_0_1_2.append( 1U , '.' ) ;
310 part_0_1_2.append( part[2] ) ;
311 part_0_1_2.append( 1U , '.' ) ;
312
313 std::string part_0_1 = part[0] ;
314 part_0_1.append( 1U , '.' ) ;
315 part_0_1.append( part[1] ) ;
316 part_0_1.append( 1U , '.' ) ;
317
318 std::string part_0 = part[0] ;
319 part_0.append( 1U , '.' ) ;
320
321 const std::string empty ;
322
323 add( result , part_0_1_2 , n3 & 0xffU , "/32" ) ;
324 add( result , part_0_1_2 , n3 & 0xfeU , "/31" ) ;
325 add( result , part_0_1_2 , n3 & 0xfcU , "/30" ) ;
326 add( result , part_0_1_2 , n3 & 0xf8U , "/29" ) ;
327 add( result , part_0_1_2 , n3 & 0xf0U , "/28" ) ;
328 add( result , part_0_1_2 , n3 & 0xe0U , "/27" ) ;
329 add( result , part_0_1_2 , n3 & 0xc0U , "/26" ) ;
330 add( result , part_0_1_2 , n3 & 0x80U , "/25" ) ;
331 add( result , part_0_1_2 , 0 , "/24" ) ;
332 add( result , part_0_1_2 , "*" ) ;
333 add( result , part_0_1 , n2 & 0xfeU , ".0/23" ) ;
334 add( result , part_0_1 , n2 & 0xfcU , ".0/22" ) ;
335 add( result , part_0_1 , n2 & 0xfcU , ".0/21" ) ;
336 add( result , part_0_1 , n2 & 0xf8U , ".0/20" ) ;
337 add( result , part_0_1 , n2 & 0xf0U , ".0/19" ) ;
338 add( result , part_0_1 , n2 & 0xe0U , ".0/18" ) ;
339 add( result , part_0_1 , n2 & 0xc0U , ".0/17" ) ;
340 add( result , part_0_1 , 0 , ".0/16" ) ;
341 add( result , part_0_1 , "*.*" ) ;
342 add( result , part_0 , n1 & 0xfeU , ".0.0/15" ) ;
343 add( result , part_0 , n1 & 0xfcU , ".0.0/14" ) ;
344 add( result , part_0 , n1 & 0xf8U , ".0.0/13" ) ;
345 add( result , part_0 , n1 & 0xf0U , ".0.0/12" ) ;
346 add( result , part_0 , n1 & 0xe0U , ".0.0/11" ) ;
347 add( result , part_0 , n1 & 0xc0U , ".0.0/10" ) ;
348 add( result , part_0 , n1 & 0x80U , ".0.0/9" ) ;
349 add( result , part_0 , 0 , ".0.0/8" ) ;
350 add( result , part_0 , "*.*.*" ) ;
351 add( result , empty , n0 & 0xfeU , ".0.0.0/7" ) ;
352 add( result , empty , n0 & 0xfcU , ".0.0.0/6" ) ;
353 add( result , empty , n0 & 0xf8U , ".0.0.0/5" ) ;
354 add( result , empty , n0 & 0xf0U , ".0.0.0/4" ) ;
355 add( result , empty , n0 & 0xe0U , ".0.0.0/3" ) ;
356 add( result , empty , n0 & 0xc0U , ".0.0.0/2" ) ;
357 add( result , empty , n0 & 0x80U , ".0.0.0/1" ) ;
358 add( result , empty , 0 , ".0.0.0/0" ) ;
359 add( result , empty , "*.*.*.*" ) ;
360
361 return result ;
362}
363
364 void GNet::Address4::add( G::StringArray & result , const std::string & head , unsigned int n , const char * tail )
365{
366 std::string s = head ;
367 s.append( G::Str::fromUInt( n ) ) ;
368 s.append( tail ) ;
369 result.push_back( s ) ;
370}
371
372 void GNet::Address4::add( G::StringArray & result , const std::string & head , const char * tail )
373{
374 result.push_back( head + tail ) ;
375}
376
377 bool GNet::Address4::format( std::string s )
378{
379 // an independent check for the IPv4 dotted-quad format
380
381 if( s.empty() || s.find_first_not_of("0123456789.") != std::string::npos ||
382 std::count(s.begin(),s.end(),'.') != 3U || s.at(0U) == '.' ||
383 s.at(s.length()-1U) == '.' || s.find("..") != std::string::npos )
384 return false ;
385
386 unsigned int n = 0U ;
387 unsigned int z = static_cast<unsigned char>('0') ;
388 for( char c : s )
389 {
390 unsigned int uc = static_cast<unsigned char>(c) ;
391 n = c == '.' ? 0U : ( ( n * 10U ) + (uc-z) ) ;
392 if( n >= 256U )
393 return false ;
394 }
395 return true ;
396}
397
398 unsigned int GNet::Address4::bits() const
399{
400 const unsigned long a = ntohl( m_inet.sin_addr.s_addr ) ;
401 unsigned int count = 0U ;
402 for( unsigned long mask = 0x80000000U ; mask && ( a & mask ) ; mask >>= 1U )
403 count++ ;
404 return count ;
405}
406
407 bool GNet::Address4::isLocal( std::string & reason ) const
408{
409 // see also GNet::Local::isLocal()
410 if( isLoopback() || isLinkLocal() || isUniqueLocal() )
411 {
412 return true ;
413 }
414 else
415 {
416 std::ostringstream ss ;
417 ss << hostPartString() << " is not in "
418 "127.0.0.0/8, 169.254.0.0/16, 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16" ;
419 reason = ss.str() ;
420 return false ;
421 }
422}
423
424 bool GNet::Address4::isLoopback() const
425{
426 // RFC-1918, RFC-6890
427 return ( ntohl(m_inet.sin_addr.s_addr) >> 24U ) == 127U ; // 127.0.0.0/8
428}
429
430 bool GNet::Address4::isLinkLocal() const
431{
432 // RFC-3927, RFC-6890
433 return ( ntohl(m_inet.sin_addr.s_addr) >> 16U ) == 0xA9FEU ; // 169.254.0.0/16
434}
435
436 bool GNet::Address4::isUniqueLocal() const
437{
438 // RFC-1918, RFC-6890
439 return
440 ( ntohl(m_inet.sin_addr.s_addr) >> 24U ) == 0x0AU || // 10.0.0.0/8
441 ( ntohl(m_inet.sin_addr.s_addr) >> 20U ) == 0xAC1U || // 172.16.0.0/12
442 ( ntohl(m_inet.sin_addr.s_addr) >> 16U ) == 0xC0A8U ; // 192.168.0.0/16
443}
444
445 bool GNet::Address4::isAny() const
446{
447 return m_inet.sin_addr.s_addr == htonl(INADDR_ANY) ;
448}
449
G::Str::join
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1195
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:405
G::Str::tail
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1287
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:1146
G::Str::fromUInt
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.h:579
G::Str::toUInt
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:604
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:444
G::Str::head
static std::string head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1273
gaddress4.h
gassert.h
glog.h
gstr.h
gtest.h
GNet
Network classes.
Definition: gdef.h:1115
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:31