4#include "Stroika/Foundation/StroikaPreComp.h"
8#if qStroika_Foundation_Common_Platform_POSIX
12#include <netinet/in.h>
14#include <sys/socket.h>
16#if qStroika_Foundation_Common_Platform_Linux
17#include <linux/netlink.h>
18#include <linux/rtnetlink.h>
20#elif qStroika_Foundation_Common_Platform_Windows
30#include "Stroika/Foundation/Containers/Collection.h"
31#include "Stroika/Foundation/Execution/Exceptions.h"
33#if qStroika_Foundation_Common_Platform_Windows
34#include "Platform/Windows/WinSock.h"
35#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
40#include "ConnectionlessSocket.h"
49using namespace Stroika::Foundation::Memory;
50using namespace Stroika::Foundation::IO;
56#pragma comment(lib, "Iphlpapi.lib")
64#include <netinet/in.h>
68#include <sys/socket.h>
72static const char* flags (
int sd,
const char* name)
74 static char buf[1024];
76 static struct ifreq ifreq;
77 strcpy (ifreq.ifr_name, name);
79 int r = ioctl (sd, SIOCGIFFLAGS, (
char*)&ifreq);
84 if (ifreq.ifr_flags & b) \
85 l += snprintf (buf + l, sizeof (buf) - l, #b " ")
90 FLAG (IFF_POINTOPOINT);
94 FLAG (IFF_NOTRAILERS);
100 FLAG (IFF_AUTOMEDIA);
109 static struct ifreq ifreqs[32] {};
110 struct ifconf ifconf {};
111 ifconf.ifc_req = ifreqs;
112 ifconf.ifc_len =
sizeof(ifreqs);
114 int sd = ::socket (PF_INET, SOCK_STREAM, 0);
117 int r = ioctl (sd, SIOCGIFCONF, (
char*)&ifconf);
120 for (
int i = 0; i < ifconf.ifc_len /
sizeof(
struct ifreq); ++i) {
121 printf (
"%s: %s\n", ifreqs[i].ifr_name, inet_ntoa (((
struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr));
122 printf (
" flags: %s\n", flags (sd, ifreqs[i].ifr_name));
131#if qStroika_Foundation_Common_Platform_Windows
138#if USE_NOISY_TRACE_IN_THIS_MODULE_
142#if qStroika_Foundation_Common_Platform_Windows
143 IO::Network::Platform::Windows::WinSock::AssureStarted ();
145 DWORD TEST = GetComputerNameEx((COMPUTER_NAME_FORMAT)cnf, buffer, &dwSize))
148 if (::gethostname (ac,
sizeof (ac)) == SOCKET_ERROR) {
149 DbgTrace (
"gethostname: err={}"_f, WSAGetLastError ());
154 if (
auto f = allNotLocal.
First ()) {
157 if (
auto f = allAddrs.
First ()) {
161#elif qStroika_Foundation_Common_Platform_POSIX
162 auto getFlags = [] (
int sd,
const char* name) ->
int {
163 struct ::ifreq ifreq{};
164 Characters::CString::Copy (ifreq.ifr_name, std::size (ifreq.ifr_name), name);
165 int r = ::ioctl (sd, SIOCGIFFLAGS, (
char*)&ifreq);
169 DbgTrace (
"ioctl on getFlags returned {}, errno={}"_f, r, errno);
173 return ifreq.ifr_flags;
176 struct ::ifreq ifreqs[32]{};
177 struct ::ifconf ifconf{};
178 ifconf.ifc_req = ifreqs;
179 ifconf.ifc_len =
sizeof (ifreqs);
181 int sd = ::socket (PF_INET, SOCK_STREAM, 0);
184 [[maybe_unused]]
int r = ::ioctl (sd, SIOCGIFCONF, (
char*)&ifconf);
188 for (
int i = 0; i < ifconf.ifc_len /
sizeof (
struct ifreq); ++i) {
189 int flags = getFlags (sd, ifreqs[i].ifr_name);
190 if ((flags & IFF_UP) and (not(flags & IFF_LOOPBACK)) and (flags & IFF_RUNNING)) {
191 result =
InternetAddress{((
struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr};
202String Network::GetPrimaryNetworkDeviceMacAddress ()
204#if USE_NOISY_TRACE_IN_THIS_MODULE_
207 [[maybe_unused]]
auto printMacAddr = [] (
const uint8_t macaddrBytes[6]) ->
String {
209 (void)std::snprintf (buf,
sizeof (buf),
"%02x:%02x:%02x:%02x:%02x:%02x", macaddrBytes[0], macaddrBytes[1], macaddrBytes[2],
210 macaddrBytes[3], macaddrBytes[4], macaddrBytes[5]);
213#if qStroika_Foundation_Common_Platform_Linux
220 ifc.ifc_len =
sizeof (buf);
222 Execution::ThrowPOSIXErrNoIfNegative (::ioctl (s.
GetNativeSocket (), SIOCGIFCONF, &ifc));
224 const struct ifreq*
const end = ifc.ifc_req + (ifc.ifc_len /
sizeof (
struct ifreq));
225 for (
const ifreq* it = ifc.ifc_req; it != end; ++it) {
227 Characters::CString::Copy (ifr.ifr_name, std::size (ifr.ifr_name), it->ifr_name);
229 if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
231 return printMacAddr (
reinterpret_cast<const uint8_t*
> (ifr.ifr_hwaddr.sa_data));
237#elif qStroika_Foundation_Common_Platform_Windows
238 IP_ADAPTER_INFO adapterInfo[10];
239 DWORD dwBufLen =
sizeof (adapterInfo);
241 for (PIP_ADAPTER_INFO pi = adapterInfo; pi !=
nullptr; pi = pi->Next) {
244 return printMacAddr (pi->Address);
252struct LinkMonitor::Rep_ {
253 void AddCallback (
const Callback& callback)
255 fCallbacks_.Add (callback);
256 StartMonitorIfNeeded_ ();
258 void RemoveCallback (
const Callback& callback)
260 fCallbacks_.Remove (callback);
264#if qStroika_Foundation_Common_Platform_POSIX
267#if qStroika_Foundation_Common_Platform_Windows
268 HANDLE fMonitorHandler_ = INVALID_HANDLE_VALUE;
271 void SendNotifies (LinkChange lc,
const String& linkName,
const String& ipAddr)
273 for (
const auto& cb : fCallbacks_) {
274 cb (lc, linkName, ipAddr);
278#if qStroika_Foundation_Common_Platform_Windows
280 static void WINAPI CB_ (
void* callerContext, PMIB_UNICASTIPADDRESS_ROW Address, MIB_NOTIFICATION_TYPE NotificationType)
282 Rep_* rep =
reinterpret_cast<Rep_*
> (callerContext);
283 if (Address != NULL) {
284 char ipAddrBuf[1024];
285 (void)snprintf (ipAddrBuf, std::size (ipAddrBuf),
"%d.%d.%d.%d", Address->Address.Ipv4.sin_addr.s_net,
286 Address->Address.Ipv4.sin_addr.s_host, Address->Address.Ipv4.sin_addr.s_lh, Address->Address.Ipv4.sin_addr.s_impno);
287 LinkChange lc = (NotificationType == MibDeleteInstance) ? LinkChange::eRemoved : LinkChange::eAdded;
293 void StartMonitorIfNeeded_ ()
295#if qStroika_Foundation_Common_Platform_Linux
296 if (fMonitorThread_ ==
nullptr) {
306 addr.nl_family = AF_NETLINK;
307 addr.nl_groups = RTMGRP_IPV4_IFADDR;
318 struct nlmsghdr* nlh;
319 nlh = (
struct nlmsghdr*)buffer;
321 while ((NLMSG_OK (nlh, len)) and (nlh->nlmsg_type != NLMSG_DONE)) {
322 if (nlh->nlmsg_type == RTM_NEWADDR) {
323 struct ifaddrmsg* ifa = (
struct ifaddrmsg*)NLMSG_DATA (nlh);
324 struct rtattr* rth = IFA_RTA (ifa);
325 int rtl = IFA_PAYLOAD (nlh);
326 while (rtl and RTA_OK (rth, rtl)) {
327 if (rth->rta_type == IFA_LOCAL) {
328 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated\"");
329 uint32_t ipaddr = htonl (*((uint32_t*)RTA_DATA (rth)));
330 DISABLE_COMPILER_CLANG_WARNING_END (
"clang diagnostic ignored \"-Wdeprecated\"");
332 ::if_indextoname (ifa->ifa_index, name);
334 char ipAddrBuf[1024];
335 ::snprintf (ipAddrBuf, std::size (ipAddrBuf),
"%d.%d.%d.%d", (ipaddr >> 24) & 0xff,
336 (ipaddr >> 16) & 0xff, (ipaddr >> 8) & 0xff, ipaddr & 0xff);
340 rth = RTA_NEXT (rth, rtl);
343 nlh = NLMSG_NEXT (nlh, len);
347 fMonitorThread_.
SetThreadName (
"Network LinkMonitor thread"sv);
348 fMonitorThread_.
Start ();
350#elif qStroika_Foundation_Common_Platform_Windows
355 if (fMonitorHandler_ == INVALID_HANDLE_VALUE) {
365#if qStroika_Foundation_Common_Platform_POSIX
367 if (fMonitorThread_ !=
nullptr) {
370#elif qStroika_Foundation_Common_Platform_Windows
371 if (fMonitorHandler_ != INVALID_HANDLE_VALUE) {
375 ::CancelMibChangeNotify2 (fMonitorHandler_);
386LinkMonitor::LinkMonitor ()
391void LinkMonitor::AddCallback (
const Callback& callback)
393 fRep_->AddCallback (callback);
396void LinkMonitor::RemoveCallback (
const Callback& callback)
398 fRep_->RemoveCallback (callback);
#define AssertNotImplemented()
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
static String FromNarrowSDKString(const char *from)
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual optional< value_type > First() const
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread ob...
nonvirtual void SetThreadName(const Characters::String &threadName) const
nonvirtual void Start() const
nonvirtual void AbortAndWaitForDone(Time::DurationSeconds timeout=Time::kInfinity) const
Abort () the thread, and then WaitForDone () - but if doesn't finish fast enough, send extra aborts (...
nonvirtual Sequence< InternetAddress > GetHostAddresses(const String &hostNameOrAddress) const
simple wrapper on GetHostEntry - looking up the hostname/ip address and returning the list of associa...
nonvirtual bool IsLinkLocalAddress() const
nonvirtual PlatformNativeHandle GetNativeSocket() const
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings.
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)
ConnectionlessSocket::Ptr New(SocketAddress::FamilyType family, Type socketKind, const optional< IPPROTO > &protocol=nullopt)
InternetAddress GetPrimaryInternetAddress()