UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
netadapter.cpp
Go to the documentation of this file.
1// Copyright (C) 2024+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
2// Redistribution only with this Copyright remark. Last modified: 2025-09-15
9#include <UPnPsdk/synclog.hpp>
10
11
12namespace UPnPsdk {
13
14uint8_t netmask_to_bitmask(const ::sockaddr_storage* a_netmask) {
15 TRACE("Executing netmask_to_bitmask()")
16 if (a_netmask == nullptr)
17 throw std::runtime_error(UPnPsdk_LOGEXCEPT(
18 "MSG1059") "No network socket address information given.\n");
19
20 uint8_t bitmask{};
21 uint8_t nullbits{};
22 switch (a_netmask->ss_family) {
23 case AF_INET6: {
24 // Count set bits.
25 for (size_t i{}; i < sizeof(in6_addr); i++) {
26 uint8_t s6addr = reinterpret_cast<const ::sockaddr_in6*>(a_netmask)
27 ->sin6_addr.s6_addr[i];
28 if (s6addr == 255) {
29 bitmask += 8;
30 } else {
31 while (s6addr) {
32 bitmask++;
33 s6addr <<= 1;
34 }
35 break; // for() loop
36 }
37 }
38 // Check if all remaining bits are zero.
39 for (int i{sizeof(in6_addr) - 1}; i >= 0; i--) {
40 uint8_t s6addr = reinterpret_cast<const ::sockaddr_in6*>(a_netmask)
41 ->sin6_addr.s6_addr[i];
42 if (s6addr == 0) {
43 nullbits += 8;
44 } else {
45 while ((s6addr & 1) == 0) {
46 nullbits++;
47 s6addr >>= 1;
48 }
49 break; // for() loop
50 }
51 }
52 // Check valid netmask.
53 if (bitmask + nullbits != 128) {
54 char ip_str[INET6_ADDRSTRLEN]{};
55 ::inet_ntop(
56 AF_INET6,
57 &reinterpret_cast<const ::sockaddr_in6*>(a_netmask)->sin6_addr,
58 ip_str, sizeof(ip_str));
59 throw std::runtime_error(
60 UPnPsdk_LOGEXCEPT(
61 "MSG1067") "Invalid ip-address prefix bitmask \"[" +
62 std::string(ip_str) + "]\".\n");
63 }
64 } break; // switch()
65
66 case AF_INET: {
67 in_addr_t saddr = ntohl(
68 reinterpret_cast<const ::sockaddr_in*>(a_netmask)->sin_addr.s_addr);
69 in_addr_t saddr0 = saddr;
70 // Count set bits.
71 while (saddr) {
72 bitmask++;
73 saddr <<= 1;
74 }
75 // Check if all remaining bits are zero.
76 for (int i{}; (saddr0 & 1) == 0 && i < 32; i++) {
77 nullbits++;
78 saddr0 >>= 1;
79 }
80 // Check valid netmask.
81 if (bitmask + nullbits != 32) {
82 char ip_str[INET_ADDRSTRLEN]{};
83 ::inet_ntop(
84 AF_INET,
85 &reinterpret_cast<const ::sockaddr_in*>(a_netmask)->sin_addr,
86 ip_str, sizeof(ip_str));
87 throw std::runtime_error(
88 UPnPsdk_LOGEXCEPT("MSG1069") "Invalid ip-address netmask \"" +
89 std::string(ip_str) + "\".\n");
90 }
91 } break;
92
93 case AF_UNSPEC:
94 return 0;
95
96 default:
97 throw std::runtime_error(
98 UPnPsdk_LOGEXCEPT("MSG1028") "Unsupported address family(" +
99 std::to_string(a_netmask->ss_family) + "), only AF_INET6(" +
100 std::to_string(AF_INET6) + "), AF_INET(" + std::to_string(AF_INET) +
101 "), or AF_UNSPEC(" + std::to_string(AF_UNSPEC) + ") are valid.\n");
102 } // switch()
103
104 return bitmask;
105}
106
107
108void bitmask_to_netmask(const ::sockaddr_storage* a_saddr,
109 const unsigned int a_prefixlength,
110 SSockaddr& a_saddrObj) {
111 TRACE("Executing bitmask_to_netmask()")
112 if (a_saddr == nullptr)
113 throw std::runtime_error(UPnPsdk_LOGEXCEPT(
114 "MSG1070") "No associated socket address for the netmask given.\n");
115
116 switch (a_saddr->ss_family) {
117 case AF_INET6: {
118 // I have to manage 16 bytes from the binary IPv6 address to set its
119 // bits. 15 bytes are all ones or all zero bits. Only one byte may have
120 // partly ones and zero bits. First I calculate how many leading bytes
121 // have full one bits. Following is the partial set byte. The rest are
122 // bytes with full zero bits set.
123 in6_addr netmask6{};
124
125 // All prefix lengths > 128 will throw this exception.
126 if (a_prefixlength > 128)
127 throw std::runtime_error(
128 UPnPsdk_LOGEXCEPT(
129 "MSG1124") "Invalid IPv6 address prefix bitmask(" +
130 std::to_string(a_prefixlength) + ") exceeds 128.\n");
131
132 // Calculate number of leading bytes with full one bits.
133 unsigned int ones_bytes{a_prefixlength / 8};
134
135 // Fill leading bytes with all bit ones.
136 unsigned int i{};
137 for (; i < ones_bytes; i++)
138 netmask6.s6_addr[i] = static_cast<uint8_t>(~0);
139
140 // Handle the one partly bit-set byte.
141 if (i < 16 /*bytes*/) {
142 // Preset byte with all bit ones.
143 netmask6.s6_addr[i] = static_cast<uint8_t>(~0);
144 // Shift remaining zero bits from right into byte with using the
145 // reminder from the byte devision.
146 netmask6.s6_addr[i] <<= (8 - (a_prefixlength % 8));
147 i++;
148
149 // Fill remaining bytes with zero bits.
150 for (; i < 16 /*bytes*/; i++)
151 netmask6.s6_addr[i] = 0;
152 }
153
154 // Return the result.
155 a_saddrObj = "";
156 a_saddrObj.sin6.sin6_family = AF_INET6;
157 memcpy(&a_saddrObj.sin6.sin6_addr, &netmask6,
158 sizeof(a_saddrObj.sin6.sin6_addr));
159 } break;
160
161 case AF_INET: {
162 // All prefix lengths > 32 will throw this exception.
163 if (a_prefixlength > 32) {
164 throw std::runtime_error(
165 UPnPsdk_LOGEXCEPT(
166 "MSG1125") "Invalid IPv4 address prefix bitmask(" +
167 std::to_string(a_prefixlength) + ") exceeds 32.\n");
168 }
169 // Prepare socket address and shift zero bits from right into the mask.
170 ::sockaddr_in saddr{};
171 saddr.sin_family = AF_INET;
172 if (a_prefixlength) {
173 in_addr_t bitshift{~0u}; // all one bits
174 bitshift <<= (32 - a_prefixlength); // shift zero bits from right
175 saddr.sin_addr.s_addr = htonl(bitshift);
176 }
177 // Return the result.
178 a_saddrObj.sin = saddr;
179 } break;
180
181 case AF_UNSPEC: {
182 a_saddrObj = "";
183 } break;
184
185 default: {
186 throw std::runtime_error(
187 UPnPsdk_LOGEXCEPT("MSG1126") "Unsupported address family(" +
188 std::to_string(a_saddr->ss_family) + "), only AF_INET6(" +
189 std::to_string(AF_INET6) + "), AF_INET(" + std::to_string(AF_INET) +
190 "), or AF_UNSPEC(" + std::to_string(AF_UNSPEC) + ") are valid.\n");
191 }
192 } // switch
193}
194
195
196// CNetadapter class
197// =================
199 : m_na_platformPtr(a_na_platformPtr) {
200 TRACE2(this, " Construct CNetadapter()") //
201}
202
203CNetadapter::~CNetadapter() {
204 TRACE2(this, " Destruct CNetadapter()")
205}
206
207void CNetadapter::get_first() { m_na_platformPtr->get_first(); }
208
209bool CNetadapter::get_next() { return m_na_platformPtr->get_next(); }
210
211unsigned int CNetadapter::index() const { return m_na_platformPtr->index(); }
212
213std::string CNetadapter::name() const { return m_na_platformPtr->name(); }
214
215void CNetadapter::sockaddr(SSockaddr& a_saddr) const {
216 SSockaddr saddr;
217 m_na_platformPtr->sockaddr(saddr);
218
219 // If there is an IPv4 address then map it to IPv6.
220 if (saddr.ss.ss_family == AF_INET) {
221 // We will get something like "[::ffff:192.168.1.2]:49494".
222 SSockaddr saddr4to6;
223 saddr4to6.sin6.sin6_family = AF_INET6;
224 saddr4to6.sin6.sin6_port = saddr.sin.sin_port;
225 in6_addr& sa6{saddr4to6.sin6.sin6_addr};
226 sa6.s6_addr[10] = 0xff; // prefix id for ipv4 mapping.
227 sa6.s6_addr[11] = 0xff; // ./.
228 // Copy ipv4 addr into ipv6 addr behind prefix.
229 ::memcpy(&sa6.s6_addr[12], &saddr.sin.sin_addr,
230 sizeof(saddr.sin.sin_addr));
231
232 a_saddr = saddr4to6;
233 } else {
234 a_saddr = saddr;
235 }
236}
237
238void CNetadapter::socknetmask(SSockaddr& a_snetmask) const {
239 SSockaddr saObj;
240 m_na_platformPtr->socknetmask(saObj);
241 if (saObj.ss.ss_family == AF_INET) // This will be V4MAPPED
242 saObj = "[ffff:ffff:ffff:ffff:ffff:ffff::]"; // V4MAPPED has bitmask 96
243 a_snetmask = saObj;
244}
245
246unsigned int CNetadapter::bitmask() const {
247 SSockaddr saObj;
248 m_na_platformPtr->sockaddr(saObj);
249 if (saObj.ss.ss_family == AF_INET) // This will be V4MAPPED
250 return 96; // V4MAPPED has bitmask 96
251 return m_na_platformPtr->bitmask();
252}
253
255void CNetadapter::reset() noexcept {
256 m_find_index = 0;
257 m_state = Find::finish;
258 m_na_platformPtr->reset();
259}
261
262
263bool CNetadapter::find_first(std::string_view a_name_or_addr) {
264 TRACE2(this, " Executing CNetadapter::find_first()")
265 SSockaddr nad_saObj;
266
267 // By default look for a valid ip address without loopback, no matter what
268 // local network adapter.
269 // -----------------------------------------------------------------------
270 // The operating system presents one as best choise.
271 if (a_name_or_addr.empty()) {
272 this->reset(); // noexcept
273 if (this->index() == 0)
274 return false; // There isn't any adapter.
275
276 do {
277 this->sockaddr(nad_saObj);
278 if (!nad_saObj.is_loopback()) {
279 m_state = Find::best;
280 return true;
281 }
282 } while (this->get_next());
283
284 // No usable address found, nothing more to do.
285 return false;
286 }
287
288 // Look for a loopback interface, no matter what local network adapter.
289 // --------------------------------------------------------------------
290 if (a_name_or_addr == "loopback") {
291 this->reset(); // noexcept
292 do {
293 this->sockaddr(nad_saObj);
294 if (nad_saObj.is_loopback()) {
295 m_state = Find::loopback;
296 return true;
297 }
298 } while (this->get_next());
299
300 // No loopback interface found, nothing more to do.
301 return false;
302 }
303
304 // Look for a local network adapter name that has at least one ip address
305 // that isn't a loopback address.
306 // ----------------------------------------------------------------------
307 this->reset(); // noexcept
308 do {
309 if (this->name() == a_name_or_addr) {
310 this->sockaddr(nad_saObj);
311 if (!nad_saObj.is_loopback()) {
312 m_find_index = this->index();
313 m_state = Find::index;
314 return true;
315 }
316 }
317 } while (this->get_next());
318 // Nothing found, reset pointer.
319 this->reset(); // noexcept
320
321 // No name found, look for the ip address of a local network adapter.
322 // ------------------------------------------------------------------
323 // Last attempt to get a socket address from the input argument.
324 SSockaddr input_saObj;
325 try { // Throws exception if invalid.
326 input_saObj = std::string(a_name_or_addr);
327 } catch (std::exception&) {
328 return false;
329 }
330 // Parse network adapter list for the given input ip address.
331 do {
332 this->sockaddr(nad_saObj);
333 if (nad_saObj == input_saObj) {
334 // With a unique ip address given, there cannot be another.
335 m_state = Find::finish;
336 return true;
337 }
338 } while (this->get_next());
339
340 return false;
341}
342
343bool CNetadapter::find_first(unsigned int a_index) {
344 TRACE2(this, " Executing CNetadapter::find_first(" +
345 std::to_string(a_index) + ")")
346 this->reset(); // noexcept
347 if (this->index() == 0)
348 return false; // There isn't any adapter.
349
350 SSockaddr nad_saObj;
351 do {
352 if (this->index() == a_index) {
353 this->sockaddr(nad_saObj);
354 if (!nad_saObj.is_loopback()) {
355 m_find_index = a_index;
356 m_state = Find::index;
357 return true;
358 }
359 }
360 } while (this->get_next());
361
362 return false;
363}
364
366 TRACE2(this, " Executing CNetadapter::find_next()")
367 SSockaddr nadap_saObj;
368
369 switch (m_state) {
370 case Find::finish:
371 return false;
372
373 case Find::best: { // without loopback
374 while (this->get_next()) {
375 this->sockaddr(nadap_saObj);
376 if (!nadap_saObj.is_loopback())
377 return true;
378 } // while
379 } break;
380
381 case Find::loopback: {
382 while (this->get_next()) {
383 this->sockaddr(nadap_saObj);
384 if (nadap_saObj.is_loopback())
385 return true;
386 } // while
387 } break;
388
389 case Find::index: { // without loopback
390 while (this->get_next()) {
391 const unsigned int index{this->index()};
392 if (index == m_find_index) {
393 this->sockaddr(nadap_saObj);
394 if (!nadap_saObj.is_loopback())
395 return true;
396 }
397 } // while
398 } break;
399 } // switch
400
401 m_state = Find::finish;
402 return false;
403}
404
405} // namespace UPnPsdk
UPnPsdk_API CNetadapter(PNetadapter_platform a_na_platformPtr=std::make_shared< CNetadapter_platform >())
Constructor.
UPnPsdk_API void get_first()
Load a list of network adapters from the operating system and select its first entry.
UPnPsdk_API void socknetmask(SSockaddr &a_snetmask) const
Get socket address netmask from current selected list entry.
UPnPsdk_API void sockaddr(SSockaddr &a_saddr) const
Get socket address from current selected list entry.
UPnPsdk_API bool find_next()
Find next ip address from local network adapters.
UPnPsdk_API unsigned int index() const
Get index number from current selected list entry.
UPnPsdk_API std::string name() const
Get network adapter name from current selected list entry.
UPnPsdk_API bool get_next()
Select next entry from the network adapter list that was initial loaded with get_first().
UPnPsdk_API bool find_first(std::string_view a_name_or_addr="")
Find local network adapter with given name or ip address.
UPnPsdk_API unsigned int bitmask() const
Get prefix length from the ip address of the current selected local network adapter.
uint8_t netmask_to_bitmask(const ::sockaddr_storage *a_netmask)
Get prefix bit number from a network address mask.
void bitmask_to_netmask(const ::sockaddr_storage *a_saddr, const unsigned int a_prefixlength, SSockaddr &a_saddrObj)
Get network address mask from address prefix bit number.
Reengineered Object Oriented UPnP+ program code.
std::shared_ptr< INetadapter > PNetadapter_platform
Smart pointer to hold the injected pointer to the netadapter object for the current used platform.
Manage information about network adapters.
Trivial ::sockaddr structures enhanced with methods.
Definition sockaddr.hpp:133
sockaddr_in & sin
Reference to sockaddr_in struct.
Definition sockaddr.hpp:141
sockaddr_in6 & sin6
Reference to sockaddr_in6 struct.
Definition sockaddr.hpp:139
bool is_loopback() const
Get if the socket address is a loopback address.
Definition sockaddr.cpp:536
sockaddr_storage & ss
Reference to sockaddr_storage struct.
Definition sockaddr.hpp:135
Define macro for synced logging to the console for detailed info and debug.