E-MailRelay

ginterfaces_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file ginterfaces_win32.cpp
19///
20// Test with:
21// netsh interface ipv4 add address name="Local Area Connection" address=10.0.0.1
22// netsh interface ipv6 add address interface="Local Area Connection" dead::beef
23// netsh interface ipv4 show addresses
24// ipconfig /all
25//
26
27#include "gdef.h"
28#include "ginterfaces.h"
29#include "gconvert.h"
30#include "gbuffer.h"
31#include "gexception.h"
32#include <iphlpapi.h>
33#include <ipifcons.h>
34
35 namespace GNet
36{
37 class InterfacesNotifierImp ;
38}
39
40 class GNet::InterfacesNotifierImp : public InterfacesNotifier
41{
42 public:
43 InterfacesNotifierImp( Interfaces * , EventState es ) ;
44 // Constructor.
45
46 ~InterfacesNotifierImp() override ;
47 // Destructor.
48
49 private: // overrides
50 std::string readEvent() override ; // unix
51 std::string onFutureEvent() override ; // windows
52
53 private:
54 static void CALLBACK interface_callback_fn( void * p , MIB_IPINTERFACE_ROW * , MIB_NOTIFICATION_TYPE ) ;
55 static void CALLBACK address_callback_fn( void * p , MIB_UNICASTIPADDRESS_ROW * , MIB_NOTIFICATION_TYPE ) ;
56
57 private:
58 static constexpr unsigned int MAGIC = 0xdeadbeef ;
59 unsigned int m_magic ;
60 HANDLE m_notify_1 ;
61 HANDLE m_notify_2 ;
62 HANDLE m_handle ;
63 FutureEvent m_future_event ;
64} ;
65
66 bool GNet::Interfaces::active()
67{
68 return true ;
69}
70
71 void GNet::Interfaces::loadImp( EventState es , std::vector<Item> & list )
72{
73 if( !m_notifier.get() )
74 m_notifier = std::make_unique<InterfacesNotifierImp>( this ,es ) ;
75
76 ULONG flags = 0 ;
77 flags |= GAA_FLAG_SKIP_ANYCAST ;
78 flags |= GAA_FLAG_SKIP_MULTICAST ;
79 flags |= GAA_FLAG_SKIP_DNS_SERVER ;
80 //flags |= GAA_FLAG_INCLUDE_ALL_INTERFACES ;
81 //flags |= GAA_FLAG_INCLUDE_PREFIX ;
82
83 G::Buffer<char> buffer ;
84 buffer.resize( 15000U ) ; // size as recommended
85 ULONG size = static_cast<ULONG>( buffer.size() ) ;
86 IP_ADAPTER_ADDRESSES * p = G::buffer_cast<IP_ADAPTER_ADDRESSES*>( buffer ) ;
87
88 ULONG rc = GetAdaptersAddresses( AF_UNSPEC , flags , /*reserved=*/nullptr , p , &size ) ;
89
90 if( rc == ERROR_BUFFER_OVERFLOW )
91 {
92 buffer.resize( size ) ;
93 p = G::buffer_cast<IP_ADAPTER_ADDRESSES*>( buffer ) ;
94 rc = GetAdaptersAddresses( AF_UNSPEC , flags , nullptr , p , &size ) ;
95 }
96
97 if( rc != ERROR_NO_DATA )
98 {
99 if( rc != ERROR_SUCCESS )
100 throw G::Exception( "GetAdaptersAddresses failed" ) ;
101
102 for( ; p ; p = p->Next )
103 {
104 Item item ;
105 item.name = std::string( p->AdapterName ) ;
106 item.altname = G::Convert::narrow( std::wstring(p->FriendlyName) ) ;
107 item.up = p->OperStatus == IfOperStatusUp ;
108 item.loopback = p->IfType == IF_TYPE_SOFTWARE_LOOPBACK ;
109
110 for( IP_ADAPTER_UNICAST_ADDRESS * ap = p->FirstUnicastAddress ; ap ; ap = ap->Next )
111 {
112 item.address_family = ap->Address.lpSockaddr->sa_family ;
113 if( Address::supports(ap->Address.lpSockaddr->sa_family,0) )
114 {
115 item.address = Address( ap->Address.lpSockaddr , ap->Address.iSockaddrLength ) ;
116 item.valid_address = !item.address.isAny() ; // just in case
117 }
118#ifndef G_MINGW
119 UINT8 prefix_length = ap->OnLinkPrefixLength ;
120 if( prefix_length <= 128U )
121 {
122 item.has_netmask = true ;
123 item.netmask_bits = prefix_length ;
124 }
125#endif
126 item.ifindex = p->IfIndex ? p->IfIndex : p->Ipv6IfIndex ;
127 list.push_back( item ) ;
128 }
129 }
130 }
131}
132
133// ==
134
135GNet::InterfacesNotifierImp::InterfacesNotifierImp( Interfaces * outer , EventState es ) :
136 m_magic(MAGIC) ,
137 m_notify_1(HNULL) ,
138 m_notify_2(HNULL) ,
139 m_handle(HNULL) ,
140 m_future_event(*outer,es)
141{
142 m_handle = m_future_event.handle() ;
143
144 NotifyIpInterfaceChange( AF_UNSPEC , &InterfacesNotifierImp::interface_callback_fn ,
145 this , FALSE , &m_notify_1 ) ;
146
147 NotifyUnicastIpAddressChange( AF_UNSPEC , &InterfacesNotifierImp::address_callback_fn ,
148 this , FALSE , &m_notify_2 ) ;
149}
150
151GNet::InterfacesNotifierImp::~InterfacesNotifierImp()
152{
153 m_magic = 0 ;
154 if( m_notify_1 ) CancelMibChangeNotify2( m_notify_1 ) ;
155 if( m_notify_2 ) CancelMibChangeNotify2( m_notify_2 ) ;
156}
157
158 void GNet::InterfacesNotifierImp::interface_callback_fn( void * this_vp , MIB_IPINTERFACE_ROW * ,
159 MIB_NOTIFICATION_TYPE )
160{
161 // worker thread -- keep it simple
162 InterfacesNotifierImp * this_ = static_cast<InterfacesNotifierImp*>(this_vp) ;
163 if( this_->m_magic == InterfacesNotifierImp::MAGIC && this_->m_handle )
164 {
165 ; // no-op -- rely on address notifications
166 }
167}
168
169 void GNet::InterfacesNotifierImp::address_callback_fn( void * this_vp , MIB_UNICASTIPADDRESS_ROW * ,
170 MIB_NOTIFICATION_TYPE )
171{
172 // worker thread -- keep it simple
173 InterfacesNotifierImp * this_ = static_cast<InterfacesNotifierImp*>(this_vp) ;
174 if( this_->m_magic == InterfacesNotifierImp::MAGIC && this_->m_handle )
175 {
176 FutureEvent::send( this_->m_handle , false ) ;
177 }
178}
179
180std::string GNet::InterfacesNotifierImp::readEvent()
181{
182 // never gets here
183 return std::string() ;
184}
185
186std::string GNet::InterfacesNotifierImp::onFutureEvent()
187{
188 return "network-change" ;
189}
190
GNet::Address::supports
static bool supports(Family) noexcept
Returns true if the implementation supports the given address family.
Definition: gaddress.cpp:33
GNet::InterfacesNotifier::readEvent
virtual std::string readEvent()=0
Called by GNet::Interfaces to handle a read event.
GNet::InterfacesNotifier::onFutureEvent
virtual std::string onFutureEvent()=0
Called by GNet::Interfaces to handle a future event.
GNet::Interfaces::active
static bool active()
Returns true if the implementation can raise InterfacesHandler events.
Definition: ginterfaces_none.cpp:44
G::Convert::narrow
static std::string narrow(const std::wstring &)
Narrows from UTF-16/UCS-4 wstring to UTF-8.
Definition: gconvert.cpp:53
G::Exception
A general-purpose exception class derived from std::exception and containing an error message.
Definition: gexception.h:64
gbuffer.h
gconvert.h
gexception.h
ginterfaces.h
GNet
Network classes.
Definition: gdef.h:1243
G::Buffer
A substitute for std::vector<char> that has more useful alignment guarantees and explicitly avoids de...
Definition: gbuffer.h:63