Stroika Library 3.0d18
 
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 ...
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
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)