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-04-16
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 m_na_platformPtr->sockaddr(a_saddr);
217}
218
219void CNetadapter::socknetmask(SSockaddr& a_snetmask) const {
220 m_na_platformPtr->socknetmask(a_snetmask);
221}
222
223unsigned int CNetadapter::bitmask() const {
224 return m_na_platformPtr->bitmask();
225}
226
227void CNetadapter::reset() noexcept {
228 m_find_index = 0;
229 m_state = Find::finish;
230 m_na_platformPtr->reset();
231}
232
233
234bool CNetadapter::find_first(std::string_view a_name_or_addr) {
235 TRACE2(this, " Executing CNetadapter::find_first()")
236 SSockaddr nad_saObj;
237
238 // By default look for a valid ip address without loopback, no matter what
239 // local network adapter.
240 // -----------------------------------------------------------------------
241 // The operating system presents one as best choise.
242 if (a_name_or_addr.empty()) {
243 this->reset(); // noexcept
244 if (this->index() == 0)
245 return false; // There isn't any adapter.
246
247 do {
248 this->sockaddr(nad_saObj);
249 if (!nad_saObj.is_loopback()) {
250 m_state = Find::best;
251 return true;
252 }
253 } while (this->get_next());
254
255 // No usable address found, nothing more to do.
256 return false;
257 }
258
259 // Look for a loopback interface, no matter what local network adapter.
260 // --------------------------------------------------------------------
261 if (a_name_or_addr == "loopback") {
262 this->reset(); // noexcept
263 do {
264 this->sockaddr(nad_saObj);
265 if (nad_saObj.is_loopback()) {
266 m_state = Find::loopback;
267 return true;
268 }
269 } while (this->get_next());
270
271 // No loopback interface found, nothing more to do.
272 return false;
273 }
274
275 // Look for a local network adapter name that has at least one ip address
276 // that isn't a loopback address.
277 // ----------------------------------------------------------------------
278 this->reset(); // noexcept
279 do {
280 if (this->name() == a_name_or_addr) {
281 this->sockaddr(nad_saObj);
282 if (!nad_saObj.is_loopback()) {
283 m_find_index = this->index();
284 m_state = Find::index;
285 return true;
286 }
287 }
288 } while (this->get_next());
289 // Nothing found, reset pointer.
290 this->reset(); // noexcept
291
292 // No name found, look for the ip address of a local network adapter.
293 // ------------------------------------------------------------------
294 // Last attempt to get a socket address from the input argument.
295 SSockaddr input_saObj;
296 try { // Throws exception if invalid.
297 input_saObj = std::string(a_name_or_addr);
298 } catch (std::exception&) {
299 return false;
300 }
301 // Parse network adapter list for the given input ip address.
302 do {
303 this->sockaddr(nad_saObj);
304 if (nad_saObj == input_saObj) {
305 // With a unique ip address given, there cannot be another.
306 m_state = Find::finish;
307 return true;
308 }
309 } while (this->get_next());
310
311 return false;
312}
313
314bool CNetadapter::find_first(unsigned int a_index) {
315 TRACE2(this, " Executing CNetadapter::find_first(" +
316 std::to_string(a_index) + ")")
317 this->reset(); // noexcept
318 if (this->index() == 0)
319 return false; // There isn't any adapter.
320
321 SSockaddr nad_saObj;
322 do {
323 if (this->index() == a_index) {
324 this->sockaddr(nad_saObj);
325 if (!nad_saObj.is_loopback()) {
326 m_find_index = a_index;
327 m_state = Find::index;
328 return true;
329 }
330 }
331 } while (this->get_next());
332
333 return false;
334}
335
337 TRACE2(this, " Executing CNetadapter::find_next()")
338 SSockaddr nadap_saObj;
339
340 switch (m_state) {
341 case Find::finish:
342 return false;
343
344 case Find::best: { // without loopback
345 while (this->get_next()) {
346 this->sockaddr(nadap_saObj);
347 if (!nadap_saObj.is_loopback())
348 return true;
349 } // while
350 } break;
351
352 case Find::loopback: {
353 while (this->get_next()) {
354 this->sockaddr(nadap_saObj);
355 if (nadap_saObj.is_loopback())
356 return true;
357 } // while
358 } break;
359
360 case Find::index: { // without loopback
361 while (this->get_next()) {
362 const unsigned int index{this->index()};
363 if (index == m_find_index) {
364 this->sockaddr(nadap_saObj);
365 if (!nadap_saObj.is_loopback())
366 return true;
367 }
368 } // while
369 } break;
370 } // switch
371
372 m_state = Find::finish;
373 return false;
374}
375
376} // 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:94
sockaddr_in & sin
Reference to sockaddr_in struct.
Definition sockaddr.hpp:102
sockaddr_in6 & sin6
Reference to sockaddr_in6 struct.
Definition sockaddr.hpp:100
bool is_loopback() const
Get if the socket address is a loopback address.
Definition sockaddr.cpp:571
Define macro for synced logging to the console for detailed info and debug.