VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gaddress4.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 // gaddress4.cpp
19 //
20 // This file is formatted for side-by-side comparison with gaddress6.cpp.
21 
22 #include "gdef.h"
23 #include "gnet.h"
24 #include "gaddress4.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 //
35 //
36 
37 namespace
38 {
39  const char * port_separators = ":/." ;
40  char port_separator = ':' ;
41 }
42 
43 unsigned short GNet::Address4::family()
44 {
45  return AF_INET ;
46 }
47 
48 int GNet::Address4::domain()
49 {
50  return PF_INET ;
51 }
52 
53 void GNet::Address4::init()
54 {
55  static specific_type zero ;
56  m_inet.specific = zero ;
57  m_inet.specific.sin_family = family() ;
58  m_inet.specific.sin_port = 0 ;
59 //
60 //
61 }
62 
63 GNet::Address4::Address4( unsigned int port )
64 {
65  init() ;
66  m_inet.specific.sin_addr.s_addr = htonl(INADDR_ANY);
67  const char * reason = setPort( m_inet , port ) ;
68  if( reason ) throw Address::Error(reason) ;
69 }
70 
71 GNet::Address4::Address4( unsigned int port , int )
72 {
73  init() ;
74  m_inet.specific.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
75  const char * reason = setPort( m_inet , port ) ;
76  if( reason ) throw Address::Error(reason) ;
77 }
78 
79 GNet::Address4::Address4( 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::Address4::Address4( const Address4 & other )
91 {
92  m_inet.specific = other.m_inet.specific ;
93 }
94 
95 GNet::Address4::Address4( 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::Address4::Address4( 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::Address4::Address4( 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::Address4::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::Address4::setHostAddress( union_type & inet , const std::string & host_part )
139 {
140  // start with a stricter check than inet_pton(), inet_addr() etc. since they allow eg. "123.123"
141  if( !Address4::format(host_part) )
142  return "invalid ipv4 network address format" ;
143 
144  int rc = inet_pton( family() , host_part.c_str() , &inet.specific.sin_addr ) ;
145  return rc == 1 ? nullptr : "invalid ipv4 network address" ;
146 }
147 
148 //
149 //
150 //
151 //
152 //
153 //
154 //
155 //
156 //
157 //
158 //
159 //
160 //
161 //
162 //
163 //
164 //
165 //
166 
167 void GNet::Address4::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::Address4::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::Address4::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.sin_port = htons( in_port ) ;
186  return nullptr ;
187 }
188 
189 //
190 //
191 //
192 //
193 //
194 //
195 
196 std::string GNet::Address4::displayString() const
197 {
198  std::ostringstream ss ;
199  ss << hostPartString() ;
200  ss << port_separator << port() ;
201  return ss.str() ;
202 }
203 
204 //
205 //
206 
207 std::string GNet::Address4::hostPartString() const
208 {
209  char buffer[INET_ADDRSTRLEN+1U] ;
210  const void * vp = & m_inet.specific.sin_addr ;
211  const char * p = inet_ntop( family() , const_cast<void*>(vp) , buffer , sizeof(buffer) ) ;
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::Address4::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::Address4::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::Address4::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::Address4::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::Address4::same( const Address4 & other ) const
251 {
252  return
253  m_inet.specific.sin_family == family() &&
254  other.m_inet.specific.sin_family == family() &&
255  sameAddr( m_inet.specific.sin_addr , other.m_inet.specific.sin_addr ) &&
256  m_inet.specific.sin_port == other.m_inet.specific.sin_port ;
257 }
258 
259 bool GNet::Address4::sameHostPart( const Address4 & other ) const
260 {
261  return
262  m_inet.specific.sin_family == family() &&
263  other.m_inet.specific.sin_family == family() &&
264  sameAddr( m_inet.specific.sin_addr , other.m_inet.specific.sin_addr ) ;
265 }
266 
267 bool GNet::Address4::sameAddr( const ::in_addr & a , const ::in_addr & b )
268 {
269  return a.s_addr == b.s_addr ;
270 }
271 
272 //
273 //
274 //
275 //
276 
277 unsigned int GNet::Address4::port() const
278 {
279  return ntohs( m_inet.specific.sin_port ) ;
280 }
281 
282 //
283 //
284 //
285 //
286 
287 const sockaddr * GNet::Address4::address() const
288 {
289  return & m_inet.general ;
290 }
291 
292 sockaddr * GNet::Address4::address()
293 {
294  return & m_inet.general ;
295 }
296 
297 socklen_t GNet::Address4::length()
298 {
299  return sizeof(specific_type) ;
300 }
301 
302 G::StringArray GNet::Address4::wildcards() const
303 {
304  std::string ip_string = hostPartString() ;
305 
306  G::StringArray result ;
307  result.reserve( 38U ) ;
308  result.push_back( ip_string ) ;
309 
310  G::StringArray part ;
311  part.reserve( 4U ) ;
312  G::Str::splitIntoFields( ip_string , part , "." ) ;
313 
314  G_ASSERT( part.size() == 4U ) ;
315  if( part.size() != 4U ||
316  part[0].empty() || !G::Str::isUInt(part[0]) ||
317  part[1].empty() || !G::Str::isUInt(part[1]) ||
318  part[2].empty() || !G::Str::isUInt(part[2]) ||
319  part[3].empty() || !G::Str::isUInt(part[3]) )
320  {
321  return result ;
322  }
323 
324  unsigned int n0 = G::Str::toUInt(part[0]) ;
325  unsigned int n1 = G::Str::toUInt(part[1]) ;
326  unsigned int n2 = G::Str::toUInt(part[2]) ;
327  unsigned int n3 = G::Str::toUInt(part[3]) ;
328 
329  std::string part_0_1_2 = part[0] ;
330  part_0_1_2.append( 1U , '.' ) ;
331  part_0_1_2.append( part[1] ) ;
332  part_0_1_2.append( 1U , '.' ) ;
333  part_0_1_2.append( part[2] ) ;
334  part_0_1_2.append( 1U , '.' ) ;
335 
336  std::string part_0_1 = part[0] ;
337  part_0_1.append( 1U , '.' ) ;
338  part_0_1.append( part[1] ) ;
339  part_0_1.append( 1U , '.' ) ;
340 
341  std::string part_0 = part[0] ;
342  part_0.append( 1U , '.' ) ;
343 
344  const std::string empty ;
345 
346  add( result , part_0_1_2 , n3 & 0xff , "/32" ) ;
347  add( result , part_0_1_2 , n3 & 0xfe , "/31" ) ;
348  add( result , part_0_1_2 , n3 & 0xfc , "/30" ) ;
349  add( result , part_0_1_2 , n3 & 0xf8 , "/29" ) ;
350  add( result , part_0_1_2 , n3 & 0xf0 , "/28" ) ;
351  add( result , part_0_1_2 , n3 & 0xe0 , "/27" ) ;
352  add( result , part_0_1_2 , n3 & 0xc0 , "/26" ) ;
353  add( result , part_0_1_2 , n3 & 0x80 , "/25" ) ;
354  add( result , part_0_1_2 , 0 , "/24" ) ;
355  add( result , part_0_1_2 , "*" ) ;
356  add( result , part_0_1 , n2 & 0xfe , ".0/23" ) ;
357  add( result , part_0_1 , n2 & 0xfc , ".0/22" ) ;
358  add( result , part_0_1 , n2 & 0xfc , ".0/21" ) ;
359  add( result , part_0_1 , n2 & 0xf8 , ".0/20" ) ;
360  add( result , part_0_1 , n2 & 0xf0 , ".0/19" ) ;
361  add( result , part_0_1 , n2 & 0xe0 , ".0/18" ) ;
362  add( result , part_0_1 , n2 & 0xc0 , ".0/17" ) ;
363  add( result , part_0_1 , 0 , ".0/16" ) ;
364  add( result , part_0_1 , "*.*" ) ;
365  add( result , part_0 , n1 & 0xfe , ".0.0/15" ) ;
366  add( result , part_0 , n1 & 0xfc , ".0.0/14" ) ;
367  add( result , part_0 , n1 & 0xf8 , ".0.0/13" ) ;
368  add( result , part_0 , n1 & 0xf0 , ".0.0/12" ) ;
369  add( result , part_0 , n1 & 0xe0 , ".0.0/11" ) ;
370  add( result , part_0 , n1 & 0xc0 , ".0.0/10" ) ;
371  add( result , part_0 , n1 & 0x80 , ".0.0/9" ) ;
372  add( result , part_0 , 0 , ".0.0/8" ) ;
373  add( result , part_0 , "*.*.*" ) ;
374  add( result , empty , n0 & 0xfe , ".0.0.0/7" ) ;
375  add( result , empty , n0 & 0xfc , ".0.0.0/6" ) ;
376  add( result , empty , n0 & 0xf8 , ".0.0.0/5" ) ;
377  add( result , empty , n0 & 0xf0 , ".0.0.0/4" ) ;
378  add( result , empty , n0 & 0xe0 , ".0.0.0/3" ) ;
379  add( result , empty , n0 & 0xc0 , ".0.0.0/2" ) ;
380  add( result , empty , n0 & 0x80 , ".0.0.0/1" ) ;
381  add( result , empty , 0 , ".0.0.0/0" ) ;
382  add( result , empty , "*.*.*.*" ) ;
383 
384  return result ;
385 }
386 
387 void GNet::Address4::add( G::StringArray & result , const std::string & head , unsigned int n , const char * tail )
388 {
389  std::string s = head ;
390  s.append( G::Str::fromUInt( n ) ) ;
391  s.append( tail ) ;
392  result.push_back( s ) ;
393 }
394 
395 void GNet::Address4::add( G::StringArray & result , const std::string & head , const char * tail )
396 {
397  result.push_back( head + tail ) ;
398 }
399 
400 bool GNet::Address4::format( std::string s )
401 {
402  // an independent check for the IPv4 dotted-quad format
403 
404  if( s.empty() || s.find_first_not_of("0123456789.") != std::string::npos ||
405  std::count(s.begin(),s.end(),'.') != 3U || s.at(0U) == '.' ||
406  s.at(s.length()-1U) == '.' || s.find("..") != std::string::npos )
407  return false ;
408 
409  unsigned int n = 0U ;
410  for( std::string::iterator pp = s.begin() ; pp != s.end() ; ++pp )
411  {
412  n = (*pp) == '.' ? 0U : ( ( n * 10U ) + (static_cast<unsigned int>(*pp)-static_cast<unsigned int>('0')) ) ;
413  if( n >= 256U )
414  return false ;
415  }
416  return true ;
417 }
418 
419 bool GNet::Address4::isLoopback() const
420 {
421  // 127.0.0.0/8
422  return ( ntohl(m_inet.specific.sin_addr.s_addr) >> 24 ) == 127U ;
423 }
424 
425 bool GNet::Address4::isLocal( std::string & reason ) const
426 {
427  // this is a dummy implementation that only tests for loopback
428  // addresses -- prefer GNet::Local::isLocal()
429  //
430  if( isLoopback() )
431  {
432  return true ;
433  }
434  else
435  {
436  std::ostringstream ss ;
437  ss << hostPartString() << " is not a loopback address" ;
438  reason = ss.str() ;
439  return false ;
440  }
441 }
442 /// \file gaddress4.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 fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.cpp:315
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 gaddress6.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
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &seperators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:921