Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
InternetAddress.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if qStroika_Foundation_Common_Platform_Windows
7#include <winsock2.h>
8
9#include <ws2tcpip.h>
10#endif
11
13#include "Stroika/Foundation/Containers/Collection.h"
15#include "Stroika/Foundation/Execution/Exceptions.h"
16#if qStroika_Foundation_Common_Platform_Windows
17#include "Platform/Windows/WinSock.h"
18#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
19#endif
21
22#include "InternetAddress.h"
23
24using namespace Stroika::Foundation;
26using namespace Stroika::Foundation::Memory;
27using namespace Stroika::Foundation::IO;
29
30#if qStroika_Foundation_Common_Platform_Windows
31// Not sure why this is necessary, but we get link errors sometimes without it... Maybe a windows makefile issue on regtest apps?
32// -- LGP 2014-11-06
33#pragma comment(lib, "Ws2_32.lib")
34#endif
35
36#if qStroika_Foundation_Common_Platform_Windows && (NTDDI_VERSION < NTDDI_VISTA)
37namespace {
38 int inet_pton (int af, const char* src, void* dst)
39 {
40 IO::Network::Platform::Windows::WinSock::AssureStarted ();
41 struct sockaddr_storage ss{};
42 int size = sizeof (ss);
43 wchar_t src_copy[INET6_ADDRSTRLEN + 1]; // stupid non-const API
44 {
45 const char* si = src;
46 for (; *si != '\0'; ++si) {
47 src_copy[si - src] = *si;
48 if (si - src >= INET6_ADDRSTRLEN) {
49 break;
50 }
51 }
52 src_copy[si - src] = '\0';
53 }
54 if (::WSAStringToAddressW (src_copy, af, NULL, (struct sockaddr*)&ss, &size) == 0) {
55 switch (af) {
56 case AF_INET:
57 *(struct in_addr*)dst = ((struct sockaddr_in*)&ss)->sin_addr;
58 return 1;
59 case AF_INET6:
60 *(struct in6_addr*)dst = ((struct sockaddr_in6*)&ss)->sin6_addr;
61 return 1;
62 }
63 }
64 return 0;
65 }
66 const char* inet_ntop (int af, const void* src, char* dst, socklen_t size)
67 {
68 IO::Network::Platform::Windows::WinSock::AssureStarted ();
69 struct sockaddr_storage ss{};
70 ss.ss_family = af;
71 switch (af) {
72 case AF_INET:
73 ((struct sockaddr_in*)&ss)->sin_addr = *(struct in_addr*)src;
74 break;
75 case AF_INET6:
76 ((struct sockaddr_in6*)&ss)->sin6_addr = *(struct in6_addr*)src;
77 break;
78 default:
79 return NULL;
80 }
81 StackBuffer<wchar_t> buf{eUninitiialized, size + 1};
82 unsigned long s = size;
83 DWORD d = WSAAddressToStringW ((struct sockaddr*)&ss, sizeof (ss), NULL, buf.begin (), &s);
84 if (d == 0) {
85 const wchar_t* si = buf.begin ();
86 Assert (s <= size_t (size));
87 for (; si < buf.begin () + s; ++si) {
88 dst[si - buf.begin ()] = static_cast<char> (*si);
89 if (si - buf.begin () >= size) {
90 break;
91 }
92 }
93 dst[si - buf.begin ()] = '\0';
94 return dst;
95 }
96 else {
97 return nullptr;
98 }
99 }
100}
101#endif
102
103/*
104 ********************************************************************************
105 *********************** IO::Network::InternetAddress ***************************
106 ********************************************************************************
107 */
108InternetAddress::InternetAddress (const string& s, AddressFamily af)
109 : fAddressFamily_{AddressFamily::UNKNOWN}
110{
111 if (not s.empty ()) {
112 if (af == AddressFamily::UNKNOWN) {
113 // guess format - based on '.' versus ':' in name
114 if (s.find ('.') != string::npos) {
115 af = AddressFamily::V4;
116 }
117 else if (s.find (':') != string::npos) {
118 af = AddressFamily::V6;
119 }
120 }
121 // queer API beware - inet_pton returns 0 (not negative) on error
122 switch (af) {
123 case AddressFamily::V4: {
124 if (::inet_pton (AF_INET, s.c_str (), &fV4_) == 0) {
125 static const auto kException_{Execution::RuntimeErrorException{"unable to parse string as IPv4 internet address"sv}};
126 Execution::Throw (kException_);
127 }
128 fAddressFamily_ = af;
129 } break;
130 case AddressFamily::V6: {
131 if (::inet_pton (AF_INET6, s.c_str (), &fV6_) == 0) {
132 static const auto kException_{Execution::RuntimeErrorException{"unable to parse string as IPv6 internet address"sv}};
133 Execution::Throw (kException_);
134 }
135 fAddressFamily_ = af;
136 } break;
137 default: {
138 // @todo need better exception
139 static const auto kException_{Execution::RuntimeErrorException{"Unrecognized address family"sv}};
140 Execution::Throw (kException_);
141 } break;
142 }
143 }
144}
145
146InternetAddress::InternetAddress (const string_view& s, AddressFamily af)
147 : InternetAddress{string{s}, af}
148{
149}
150
151InternetAddress::InternetAddress (const char* s, AddressFamily af)
152 : InternetAddress{string{s}, af}
153{
154}
155
156InternetAddress::InternetAddress (const String& s, AddressFamily af)
157 : InternetAddress{s.AsUTF8<string> (), af}
158{
159}
160
162
163 template <>
164 vector<bool> InternetAddress::As<vector<bool>> () const
165 {
166 if (GetAddressSize ().has_value ()) {
167 size_t sz = *GetAddressSize ();
168 vector<bool> result;
169 result.reserve (sz * 8);
170 for (uint8_t b : As<vector<uint8_t>> ()) {
171 // We could logically have numbered bits either way, but since this returns high order byte first only makes
172 // sense to return high order bit first
173 for (unsigned int i = 0; i < 8; ++i) {
174 result.push_back (BitSubstring (b, 7 - i, 7 - i + 1));
175 }
176 }
177 return result;
178 }
179 else {
180 return vector<bool>{};
181 }
182 }
183 template <>
184 String InternetAddress::As<String> () const
185 {
186 switch (fAddressFamily_) {
187 case AddressFamily::UNKNOWN: {
188 return String{};
189 } break;
190 case AddressFamily::V4: {
191 char buf[INET_ADDRSTRLEN];
192 const char* result = ::inet_ntop (AF_INET, &fV4_, buf, sizeof (buf));
193 Assert (result != nullptr); // no need to throw, because according to list of errors in http://man7.org/linux/man-pages/man3/inet_ntop.3.html cannot be error
194 Assert (::strlen (buf) < sizeof (buf)); // docs don't say explicitly, but assuming it nul-terminates
195 Assert (result == buf);
196 return String{result};
197 } break;
198 case AddressFamily::V6: {
199 char buf[INET6_ADDRSTRLEN];
200 const char* result = ::inet_ntop (AF_INET6, &fV6_, buf, sizeof (buf));
201 Assert (result != nullptr); // no need to throw, because according to list of errors in http://man7.org/linux/man-pages/man3/inet_ntop.3.html cannot be error
202 Assert (result == buf);
203 Assert (::strlen (buf) < sizeof (buf)); // docs don't say explicitly, but assuming it nul-terminates
204 return String{result};
205 } break;
206 default: {
208 return String{};
209 } break;
210 }
211 }
212
213}
214
216{
217 Require (not empty ());
218 switch (fAddressFamily_) {
219 case AddressFamily::V4: {
220 // 127.0.0.x
221 array<uint8_t, 4> octets = As<array<uint8_t, 4>> ();
222 return octets[0] == 0x7f and octets[1] == 0x0 and octets[2] == 0x0;
223 } break;
224 case AddressFamily::V6: {
225 return fV6_.s6_addr[0] == 0 and fV6_.s6_addr[1] == 0 and fV6_.s6_addr[2] == 0 and fV6_.s6_addr[3] == 0 and
226 fV6_.s6_addr[4] == 0 and fV6_.s6_addr[5] == 0 and fV6_.s6_addr[6] == 0 and fV6_.s6_addr[7] == 0 and
227 fV6_.s6_addr[8] == 0 and fV6_.s6_addr[9] == 0 and fV6_.s6_addr[10] == 0 and fV6_.s6_addr[11] == 0 and
228 fV6_.s6_addr[12] == 0 and fV6_.s6_addr[13] == 0 and fV6_.s6_addr[14] == 0 and fV6_.s6_addr[15] == 1;
229 } break;
230 }
232 return false;
233}
234
236{
237 Require (not empty ());
238 switch (fAddressFamily_) {
239 case AddressFamily::V4: {
240 static constexpr InternetAddress kMinLinkLocal_{169, 254, 0, 1};
241 static constexpr InternetAddress kMaxLinkLocal_{169, 254, 255, 254};
242 Assert (kMinLinkLocal_ < kMaxLinkLocal_);
243 return kMinLinkLocal_ <= *this and * this <= kMaxLinkLocal_;
244 } break;
245 case AddressFamily::V6: {
246 return fV6_.s6_addr[0] == 0xfe and fV6_.s6_addr[1] == 0x80 and fV6_.s6_addr[2] == 0x0 and fV6_.s6_addr[3] == 0x0 and
247 fV6_.s6_addr[4] == 0x0 and fV6_.s6_addr[5] == 0x0 and fV6_.s6_addr[6] == 0x0 and fV6_.s6_addr[7] == 0x0;
248 } break;
249 }
251 return false;
252}
253
255{
256 switch (fAddressFamily_) {
257 case AddressFamily::V4: {
258 /*
259 * http://www.faqs.org/rfcs/rfc1918.html
260 *
261 * 3. Private Address Space
262 *
263 * The Internet Assigned Numbers Authority (IANA) has reserved the
264 * following three blocks of the IP address space for private internets:
265 *
266 * 10.0.0.0 - 10.255.255.255 (10/8 prefix)
267 * 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
268 * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
269 */
270 array<uint8_t, 4> octets = As<array<uint8_t, 4>> ();
271 if (octets[0] == 10) {
272 return true;
273 }
274 if (octets[0] == 172 and (octets[1] & 0xf0) == 16) {
275 return true;
276 }
277 if (octets[0] == 192 and octets[1] == 168) {
278 return true;
279 }
280 return false;
281 } break;
282 case AddressFamily::V6: {
283 /*
284 * From http://en.wikipedia.org/wiki/Private_network
285 *
286 * The concept of private networks and special address reservation for such networks
287 * has been carried over to the next generation of the Internet Protocol, IPv6.
288 * The address block fc00:: / 7 has been reserved by IANA as described in RFC 4193.
289 * These addresses are called Unique Local Addresses (ULA).They are defined as being
290 * unicast in character and contain a 40 - bit random number in the routing prefix.
291 */
292 bool result = (fV6_.s6_addr[0] == 0xfc or fV6_.s6_addr[0] == 0xfd) and fV6_.s6_addr[1] == 0x0;
293 return result;
294 } break;
295 }
297 return false;
298}
299
301{
302 Require (not empty ());
303 switch (fAddressFamily_) {
304 case AddressFamily::V4: {
305 // From http://en.wikipedia.org/wiki/Multicast_address :
306 // The group includes the addresses from 224.0.0.0 to 239.255.255.255
307 array<uint8_t, 4> octets = As<array<uint8_t, 4>> ();
308 return 224 <= octets[0] and octets[0] <= 239;
309 } break;
310 case AddressFamily::V6: {
311 return fV6_.s6_addr[0] == 0xff;
312 } break;
313 }
315 return false;
316}
317
318optional<InternetAddress> InternetAddress::AsAddressFamily (AddressFamily family) const
319{
320 if (GetAddressFamily () == family) {
321 return *this;
322 }
323 if (GetAddressFamily () == AddressFamily::V4 and family == AddressFamily::V6) {
324 /*
325 * See https://en.wikipedia.org/wiki/6to4
326 */
327 array<uint8_t, 4> octets = As<array<uint8_t, 4>> ();
328 return InternetAddress{in6_addr{{{0x20, 0x02, octets[0], octets[1], octets[2], octets[3]}}}};
329 }
330 else if (GetAddressFamily () == AddressFamily::V6 and family == AddressFamily::V4) {
331 /*
332 * See https://en.wikipedia.org/wiki/6to4
333 *
334 * Note: there are other mappings we should support
335 */
336 in6_addr tmp = As<in6_addr> ();
337 if (tmp.s6_addr[0] == 0x20 and tmp.s6_addr[1] == 0x02) {
338 return InternetAddress{tmp.s6_addr[2], tmp.s6_addr[3], tmp.s6_addr[4], tmp.s6_addr[5]};
339 }
340 }
341 // @todo - other cases - can SOMETIMES be done!!!
342 return nullopt;
343}
344
346{
347 if (V4::kAddrAny == *this) {
348 return "INADDR_ANY"sv;
349 }
350 if (V6::kAddrAny == *this) {
351 return "in6addr_any"sv;
352 }
353 if (V4::kLocalhost == *this) {
354 return "localhost"sv;
355 }
356 if (V6::kLocalhost == *this) {
357 return "v6-localhost"sv; // no well-defined constant for this, but a good guess
358 }
359 if (V6::kV4MappedLocalhost == *this) {
360 return "v4-localhost-As-v6"sv; // no well-defined constant for this, but a good guess
361 }
362 return As<String> ();
363}
364
365InternetAddress InternetAddress::KeepSignificantBits (unsigned int significantBits) const
366{
367 // Mask address by significant bits
369 unsigned int sigBitsLeft = significantBits;
370 for (uint8_t b : this->As<vector<uint8_t>> ()) {
371 if (sigBitsLeft >= 8) {
372 r.push_back (b);
373 sigBitsLeft -= 8;
374 }
375 else {
376 unsigned int topBit = 8;
377 unsigned int botBit = topBit - sigBitsLeft;
378 r.push_back (BitSubstring<uint8_t> (b, botBit, topBit) << botBit);
379 sigBitsLeft = 0;
380 }
381 }
382 return InternetAddress{r, this->GetAddressFamily ()};
383}
384
386{
387 vector<uint8_t> addressAsArrayOfBytes = As<vector<uint8_t>> (); // As<> defined to return high order bytes first
388 Require (addressAsArrayOfBytes.size () >= 4);
389 size_t idx = addressAsArrayOfBytes.size () - 1;
390 while (o != 0) {
391 unsigned int bytePart = o % 256;
392 o -= bytePart;
393 unsigned int sum = addressAsArrayOfBytes[idx] + bytePart;
394 addressAsArrayOfBytes[idx] = sum % 256;
395 o += (sum / 256) * 256; // carry
396 if (idx == 0) {
397 break;
398 }
399 else {
400 idx--;
401 o >>= 8;
402 }
403 }
404 return InternetAddress{addressAsArrayOfBytes, GetAddressFamily ()};
405}
406
408{
409 vector<uint8_t> addressAsArrayOfBytes = As<vector<uint8_t>> ();
410 Require (addressAsArrayOfBytes.size () >= 4);
411 Require (o <= addressAsArrayOfBytes.size () * 8);
412 size_t idx = addressAsArrayOfBytes.size () - 1;
413 unsigned int bitsRemaining = o;
414 // set all bits, starting at low order bits - to ones
415 while (bitsRemaining != 0) {
416 Assert (idx > 0 and idx < addressAsArrayOfBytes.size ());
417 auto nBits = Math::AtMost (bitsRemaining, 8u);
418 addressAsArrayOfBytes[idx] = addressAsArrayOfBytes[idx] | Memory::BitSubstring<uint8_t> (0xff, 0, nBits);
419 idx--;
420 bitsRemaining -= nBits;
421 }
422 return InternetAddress{addressAsArrayOfBytes, GetAddressFamily ()};
423}
424
425/*
426 ********************************************************************************
427 ********************* IO::Network::InternetAddresses_Any ***********************
428 ********************************************************************************
429 */
431{
433 if (InternetProtocol::IP::SupportIPV4 (ipSupport)) {
434 result += V4::kAddrAny;
435 }
436 if (InternetProtocol::IP::SupportIPV6 (ipSupport)) {
437 result += V6::kAddrAny;
438 }
439 return move (result);
440}
441
442/*
443 ********************************************************************************
444 ****************** IO::Network::InternetAddresses_Localhost ********************
445 ********************************************************************************
446 */
448{
450 if (InternetProtocol::IP::SupportIPV4 (ipSupport)) {
451 result += V4::kLocalhost;
452 }
453 if (InternetProtocol::IP::SupportIPV6 (ipSupport)) {
454 result += V6::kLocalhost;
455 }
456 return move (result);
457}
458
459/*
460 ********************************************************************************
461 ********** hash<Stroika::Foundation::IO::Network::InternetAddress> *************
462 ********************************************************************************
463 */
464size_t std::hash<Stroika::Foundation::IO::Network::InternetAddress>::operator() (const Stroika::Foundation::IO::Network::InternetAddress& arg) const
465{
466 return hash<Characters::String>{}(arg.As<Characters::String> ());
467}
468
469/*
470 ********************************************************************************
471 ******** DataExchange::DefaultSerializer<IO::Network::InternetAddress> *********
472 ********************************************************************************
473 */
475{
476 return DefaultSerializer<Characters::String>{}(arg.As<Characters::String> ());
477}
#define RequireNotReached()
Definition Assertions.h:385
#define AssertNotReached()
Definition Assertions.h:355
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
nonvirtual InternetAddress PinLowOrderBitsToMax(unsigned int o) const
offset this IP Address by 'o' by setting the low order 'o' bits to the maximum value
nonvirtual optional< InternetAddress > AsAddressFamily(AddressFamily family) const
nonvirtual InternetAddress Offset(uint64_t o) const
offset this IP Address by 'o' discrete addresses (positive only, unsigned offset).
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void push_back(Common::ArgByValueType< T > e)
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
bool SupportIPV4(IPVersionSupport flag)
Definition IP.inl:8
Traversal::Iterable< InternetAddress > InternetAddresses_Localhost(InternetProtocol::IP::IPVersionSupport ipSupport=InternetProtocol::IP::IPVersionSupport::eDEFAULT)
Traversal::Iterable< InternetAddress > InternetAddresses_Any(InternetProtocol::IP::IPVersionSupport ipSupport=InternetProtocol::IP::IPVersionSupport::eDEFAULT)