E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gaddress4.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 // gaddress4.cpp
19 //
20 // This file is formatted for side-by-side comparison with gaddress6.cpp.
21
22 #include "gdef.h"
23 #include "gaddress4.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 //
34 //
35
36 namespace
37 {
38 const char * port_separators = ":/" ;
39 char port_separator = ':' ;
40 }
41
42 unsigned short GNet::Address4::family()
43 {
44 return AF_INET ;
45 }
46
47 int GNet::Address4::domain()
48 {
49 return PF_INET ;
50 }
51
52 void GNet::Address4::init()
53 {
54 static specific_type zero ;
55 m_inet.specific = zero ;
56 m_inet.specific.sin_family = family() ;
57 m_inet.specific.sin_port = 0 ;
58 //
59 //
60 }
61
62 GNet::Address4::Address4( unsigned int port )
63 {
64 init() ;
65 m_inet.specific.sin_addr.s_addr = htonl(INADDR_ANY);
66 const char * reason = setPort( m_inet , port ) ;
67 if( reason ) throw Address::Error(reason) ;
68 }
69
70 GNet::Address4::Address4( unsigned int port , int )
71 {
72 init() ;
73 m_inet.specific.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
74 const char * reason = setPort( m_inet , port ) ;
75 if( reason ) throw Address::Error(reason) ;
76 }
77
78 GNet::Address4::Address4( 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::Address4::Address4( const Address4 & other )
90 {
91 m_inet.specific = other.m_inet.specific ;
92 }
93
94 GNet::Address4::Address4( 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::Address4::Address4( 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::Address4::Address4( 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::Address4::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::Address4::setHostAddress( union_type & inet , const std::string & host_part )
138 {
139 // start with a stricter check than inet_pton(), inet_addr() etc. since they allow eg. "123.123"
140 if( !Address4::format(host_part) )
141 return "invalid ipv4 network address format" ;
142
143 int rc = inet_pton( family() , host_part.c_str() , &inet.specific.sin_addr ) ;
144 return rc == 1 ? nullptr : "invalid ipv4 network address" ;
145 }
146
147 void GNet::Address4::setPort( unsigned int port )
148 {
149 const char * reason = setPort( m_inet , port ) ;
150 if( reason )
151 throw Address::Error( "invalid port number" ) ;
152 }
153
154 const char * GNet::Address4::setPort( union_type & inet , const std::string & port_part )
155 {
156 if( port_part.length() == 0U ) return "empty port string" ;
157 if( !G::Str::isNumeric(port_part) || !G::Str::isUInt(port_part) ) return "non-numeric port string" ;
158 return setPort( inet , G::Str::toUInt(port_part) ) ;
159 }
160
161 const char * GNet::Address4::setPort( union_type & inet , unsigned int port )
162 {
163 if( port > 0xFFFFU ) return "port number too big" ;
164 const g_port_t in_port = static_cast<g_port_t>(port) ;
165 inet.specific.sin_port = htons( in_port ) ;
166 return nullptr ;
167 }
168
169 std::string GNet::Address4::displayString() const
170 {
171 std::ostringstream ss ;
172 ss << hostPartString() ;
173 ss << port_separator << port() ;
174 return ss.str() ;
175 }
176
177 std::string GNet::Address4::hostPartString() const
178 {
179 char buffer[INET_ADDRSTRLEN+1U] ;
180 const void * vp = & m_inet.specific.sin_addr ;
181 const char * p = inet_ntop( family() , const_cast<void*>(vp) , buffer , sizeof(buffer) ) ;
182 if( p == nullptr )
183 throw Address::Error( "inet_ntop() failure" ) ;
184 buffer[sizeof(buffer)-1U] = '\0' ;
185 return std::string(buffer) ;
186 }
187
188 bool GNet::Address4::validData( const sockaddr * addr , socklen_t len )
189 {
190 return addr != nullptr && addr->sa_family == family() && len == sizeof(specific_type) ;
191 }
192
193 bool GNet::Address4::validString( const std::string & s , std::string * reason_p )
194 {
195 union_type inet ;
196 const char * reason = setAddress( inet , s ) ;
197 if( reason && reason_p )
198 *reason_p = std::string(reason) ;
199 return reason == nullptr ;
200 }
201
202 bool GNet::Address4::validStrings( const std::string & host_part , const std::string & port_part , std::string * reason_p )
203 {
204 union_type inet ;
205 const char * reason = setHostAddress( inet , host_part ) ;
206 if( !reason )
207 reason = setPort( inet , port_part ) ;
208 if( reason && reason_p )
209 *reason_p = std::string(reason) ;
210 return reason == nullptr ;
211 }
212
213 bool GNet::Address4::validPort( unsigned int port )
214 {
215 union_type inet ;
216 const char * reason = setPort( inet , port ) ;
217 return reason == nullptr ;
218 }
219
220 bool GNet::Address4::same( const Address4 & other ) const
221 {
222 return
223 m_inet.specific.sin_family == family() &&
224 other.m_inet.specific.sin_family == family() &&
225 sameAddr( m_inet.specific.sin_addr , other.m_inet.specific.sin_addr ) &&
226 m_inet.specific.sin_port == other.m_inet.specific.sin_port ;
227 }
228
229 bool GNet::Address4::sameHostPart( const Address4 & other ) const
230 {
231 return
232 m_inet.specific.sin_family == family() &&
233 other.m_inet.specific.sin_family == family() &&
234 sameAddr( m_inet.specific.sin_addr , other.m_inet.specific.sin_addr ) ;
235 }
236
237 bool GNet::Address4::sameAddr( const ::in_addr & a , const ::in_addr & b )
238 {
239 return a.s_addr == b.s_addr ;
240 }
241
242 unsigned int GNet::Address4::port() const
243 {
244 return ntohs( m_inet.specific.sin_port ) ;
245 }
246
247 const sockaddr * GNet::Address4::address() const
248 {
249 return & m_inet.general ;
250 }
251
252 sockaddr * GNet::Address4::address()
253 {
254 return & m_inet.general ;
255 }
256
257 socklen_t GNet::Address4::length()
258 {
259 return sizeof(specific_type) ;
260 }
261
262 G::StringArray GNet::Address4::wildcards() const
263 {
264 std::string ip_string = hostPartString() ;
265
266 G::StringArray result ;
267 result.reserve( 38U ) ;
268 result.push_back( ip_string ) ;
269
270 G::StringArray part ;
271 part.reserve( 4U ) ;
272 G::Str::splitIntoFields( ip_string , part , "." ) ;
273
274 G_ASSERT( part.size() == 4U ) ;
275 if( part.size() != 4U ||
276 part[0].empty() || !G::Str::isUInt(part[0]) ||
277 part[1].empty() || !G::Str::isUInt(part[1]) ||
278 part[2].empty() || !G::Str::isUInt(part[2]) ||
279 part[3].empty() || !G::Str::isUInt(part[3]) )
280 {
281 return result ;
282 }
283
284 unsigned int n0 = G::Str::toUInt(part[0]) ;
285 unsigned int n1 = G::Str::toUInt(part[1]) ;
286 unsigned int n2 = G::Str::toUInt(part[2]) ;
287 unsigned int n3 = G::Str::toUInt(part[3]) ;
288
289 std::string part_0_1_2 = part[0] ;
290 part_0_1_2.append( 1U , '.' ) ;
291 part_0_1_2.append( part[1] ) ;
292 part_0_1_2.append( 1U , '.' ) ;
293 part_0_1_2.append( part[2] ) ;
294 part_0_1_2.append( 1U , '.' ) ;
295
296 std::string part_0_1 = part[0] ;
297 part_0_1.append( 1U , '.' ) ;
298 part_0_1.append( part[1] ) ;
299 part_0_1.append( 1U , '.' ) ;
300
301 std::string part_0 = part[0] ;
302 part_0.append( 1U , '.' ) ;
303
304 const std::string empty ;
305
306 add( result , part_0_1_2 , n3 & 0xff , "/32" ) ;
307 add( result , part_0_1_2 , n3 & 0xfe , "/31" ) ;
308 add( result , part_0_1_2 , n3 & 0xfc , "/30" ) ;
309 add( result , part_0_1_2 , n3 & 0xf8 , "/29" ) ;
310 add( result , part_0_1_2 , n3 & 0xf0 , "/28" ) ;
311 add( result , part_0_1_2 , n3 & 0xe0 , "/27" ) ;
312 add( result , part_0_1_2 , n3 & 0xc0 , "/26" ) ;
313 add( result , part_0_1_2 , n3 & 0x80 , "/25" ) ;
314 add( result , part_0_1_2 , 0 , "/24" ) ;
315 add( result , part_0_1_2 , "*" ) ;
316 add( result , part_0_1 , n2 & 0xfe , ".0/23" ) ;
317 add( result , part_0_1 , n2 & 0xfc , ".0/22" ) ;
318 add( result , part_0_1 , n2 & 0xfc , ".0/21" ) ;
319 add( result , part_0_1 , n2 & 0xf8 , ".0/20" ) ;
320 add( result , part_0_1 , n2 & 0xf0 , ".0/19" ) ;
321 add( result , part_0_1 , n2 & 0xe0 , ".0/18" ) ;
322 add( result , part_0_1 , n2 & 0xc0 , ".0/17" ) ;
323 add( result , part_0_1 , 0 , ".0/16" ) ;
324 add( result , part_0_1 , "*.*" ) ;
325 add( result , part_0 , n1 & 0xfe , ".0.0/15" ) ;
326 add( result , part_0 , n1 & 0xfc , ".0.0/14" ) ;
327 add( result , part_0 , n1 & 0xf8 , ".0.0/13" ) ;
328 add( result , part_0 , n1 & 0xf0 , ".0.0/12" ) ;
329 add( result , part_0 , n1 & 0xe0 , ".0.0/11" ) ;
330 add( result , part_0 , n1 & 0xc0 , ".0.0/10" ) ;
331 add( result , part_0 , n1 & 0x80 , ".0.0/9" ) ;
332 add( result , part_0 , 0 , ".0.0/8" ) ;
333 add( result , part_0 , "*.*.*" ) ;
334 add( result , empty , n0 & 0xfe , ".0.0.0/7" ) ;
335 add( result , empty , n0 & 0xfc , ".0.0.0/6" ) ;
336 add( result , empty , n0 & 0xf8 , ".0.0.0/5" ) ;
337 add( result , empty , n0 & 0xf0 , ".0.0.0/4" ) ;
338 add( result , empty , n0 & 0xe0 , ".0.0.0/3" ) ;
339 add( result , empty , n0 & 0xc0 , ".0.0.0/2" ) ;
340 add( result , empty , n0 & 0x80 , ".0.0.0/1" ) ;
341 add( result , empty , 0 , ".0.0.0/0" ) ;
342 add( result , empty , "*.*.*.*" ) ;
343
344 return result ;
345 }
346
347 void GNet::Address4::add( G::StringArray & result , const std::string & head , unsigned int n , const char * tail )
348 {
349 std::string s = head ;
350 s.append( G::Str::fromUInt( n ) ) ;
351 s.append( tail ) ;
352 result.push_back( s ) ;
353 }
354
355 void GNet::Address4::add( G::StringArray & result , const std::string & head , const char * tail )
356 {
357 result.push_back( head + tail ) ;
358 }
359
360 bool GNet::Address4::format( std::string s )
361 {
362 // an independent check for the IPv4 dotted-quad format
363
364 if( s.empty() || s.find_first_not_of("0123456789.") != std::string::npos ||
365 std::count(s.begin(),s.end(),'.') != 3U || s.at(0U) == '.' ||
366 s.at(s.length()-1U) == '.' || s.find("..") != std::string::npos )
367 return false ;
368
369 unsigned int n = 0U ;
370 for( std::string::iterator pp = s.begin() ; pp != s.end() ; ++pp )
371 {
372 n = (*pp) == '.' ? 0U : ( ( n * 10U ) + (static_cast<unsigned int>(*pp)-static_cast<unsigned int>('0')) ) ;
373 if( n >= 256U )
374 return false ;
375 }
376 return true ;
377 }
378
379 bool GNet::Address4::isLoopback() const
380 {
381 // 127.0.0.0/8
382 return ( ntohl(m_inet.specific.sin_addr.s_addr) >> 24 ) == 127U ;
383 }
384
385 bool GNet::Address4::isLocal( std::string & reason ) const
386 {
387 // this is a dummy implementation that only tests for loopback
388 // addresses -- prefer GNet::Local::isLocal()
389 //
390 if( isLoopback() )
391 {
392 return true ;
393 }
394 else
395 {
396 std::ostringstream ss ;
397 ss << hostPartString() << " is not a loopback address" ;
398 reason = ss.str() ;
399 return false ;
400 }
401 }
402 /// \file gaddress4.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
G::Str::fromUInt
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.cpp:353
gstr.h
gassert.h
gaddress4.h
This file is formatted for side-by-side comparison with gaddress6.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
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &separators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:975
gdebug.h