E-MailRelay
ginterfaces_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 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
35namespace GNet
36{
37 class InterfacesNotifierImp ;
38}
39
40class GNet::InterfacesNotifierImp : public InterfacesNotifier
41{
42public:
43 InterfacesNotifierImp( Interfaces * , ExceptionSink es ) ;
44 // Constructor.
45
46 ~InterfacesNotifierImp() override ;
47 // Destructor.
48
49private: // overrides
50 std::string readEvent() override ; // unix
51 std::string onFutureEvent() override ; // windows
52
53private:
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
57private:
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
67{
68 return true ;
69}
70
71void GNet::Interfaces::loadImp( ExceptionSink 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 G::Convert::utf8 altname ;
107 G::Convert::convert( altname , std::wstring(p->FriendlyName) ) ;
108 item.altname = altname.s ;
109 item.up = p->OperStatus == IfOperStatusUp ;
110 item.loopback = p->IfType == IF_TYPE_SOFTWARE_LOOPBACK ;
111
112 for( IP_ADAPTER_UNICAST_ADDRESS * ap = p->FirstUnicastAddress ; ap ; ap = ap->Next )
113 {
114 item.address_family = ap->Address.lpSockaddr->sa_family ;
115 if( Address::supports(ap->Address.lpSockaddr->sa_family,0) )
116 {
117 item.address = Address( ap->Address.lpSockaddr , ap->Address.iSockaddrLength ) ;
118 item.valid_address = !item.address.isAny() ; // just in case
119 }
120#ifndef G_MINGW
121 UINT8 prefix_length = ap->OnLinkPrefixLength ;
122 if( prefix_length <= 128U )
123 {
124 item.has_netmask = true ;
125 item.netmask_bits = prefix_length ;
126 }
127#endif
128 item.ifindex = p->IfIndex ? p->IfIndex : p->Ipv6IfIndex ;
129 list.push_back( item ) ;
130 }
131 }
132 }
133}
134
135// ==
136
137GNet::InterfacesNotifierImp::InterfacesNotifierImp( Interfaces * outer , ExceptionSink es ) :
138 m_magic(MAGIC) ,
139 m_notify_1(HNULL) ,
140 m_notify_2(HNULL) ,
141 m_handle(HNULL) ,
142 m_future_event(*outer,es)
143{
144 m_handle = m_future_event.handle() ;
145
146 NotifyIpInterfaceChange( AF_UNSPEC , &InterfacesNotifierImp::interface_callback_fn ,
147 this , FALSE , &m_notify_1 ) ;
148
149 NotifyUnicastIpAddressChange( AF_UNSPEC , &InterfacesNotifierImp::address_callback_fn ,
150 this , FALSE , &m_notify_2 ) ;
151}
152
153GNet::InterfacesNotifierImp::~InterfacesNotifierImp()
154{
155 m_magic = 0 ;
156 if( m_notify_1 ) CancelMibChangeNotify2( m_notify_1 ) ;
157 if( m_notify_2 ) CancelMibChangeNotify2( m_notify_2 ) ;
158}
159
160void GNet::InterfacesNotifierImp::interface_callback_fn( void * this_vp , MIB_IPINTERFACE_ROW * ,
161 MIB_NOTIFICATION_TYPE )
162{
163 // worker thread -- keep it simple
164 InterfacesNotifierImp * this_ = static_cast<InterfacesNotifierImp*>(this_vp) ;
165 if( this_->m_magic == InterfacesNotifierImp::MAGIC && this_->m_handle )
166 {
167 ; // no-op -- rely on address notifications
168 }
169}
170
171void GNet::InterfacesNotifierImp::address_callback_fn( void * this_vp , MIB_UNICASTIPADDRESS_ROW * ,
172 MIB_NOTIFICATION_TYPE )
173{
174 // worker thread -- keep it simple
175 InterfacesNotifierImp * this_ = static_cast<InterfacesNotifierImp*>(this_vp) ;
176 if( this_->m_magic == InterfacesNotifierImp::MAGIC && this_->m_handle )
177 {
178 FutureEvent::send( this_->m_handle , false ) ;
179 }
180}
181
183{
184 // never gets here
185 return std::string() ;
186}
187
189{
190 return "network-change" ;
191}
192
static bool supports(Family) noexcept
Returns true if the implementation supports the given address family.
Definition: gaddress.cpp:33
virtual std::string readEvent()=0
Called by GNet::Interfaces to handle a read event.
virtual std::string onFutureEvent()=0
Called by GNet::Interfaces to handle a future event.
static bool active()
Returns true if the implementation can raise InterfacesHandler events.
static void convert(utf8 &utf_out, const std::string &in_)
Converts between string types/encodings: native to utf8.
Definition: gconvert.cpp:44
A general-purpose exception class derived from std::exception and containing an error message.
Definition: gexception.h:64
Network classes.
Definition: gdef.h:1144
A substitute for std::vector<char> that has more useful alignment guarantees and explicitly avoids de...
Definition: gbuffer.h:63
A string wrapper that indicates UTF-8 encoding.
Definition: gconvert.h:65