1 // When compiled with the v100_xp platorm on vista and above the winver
2 // check will fail(NotifyIpInterfaceChange is not available below Vista)
3
4 #if (WINVER >= 0x0600) // Win32NetMonitor only works for Windows Vista and higher
5 class Win32NetMonitor : public ProtoNet::Monitor
6 {
7 public:
8 Win32NetMonitor();
9 ~Win32NetMonitor();
10
11 bool Open();
12 void Close();
13 bool GetNextEvent(Event& theEvent);
14 bool GetEvent(PMIB_IPINTERFACE_ROW row,
15 MIB_NOTIFICATION_TYPE notificationType);
16 static bool FindIPAddr(NET_IFINDEX InterfaceIndex);
17 const char* GetNotificationType(int type);
GetEventHandle()18 HANDLE GetEventHandle() { return input_handle; }
19
20 private:
21 // We cache mib changes to a linked list for
22 // retrieval by the GetNextEvent() method
23 class EventItem : public Event, public ProtoList::Item
24 {
25 public:
26 EventItem();
27 ~EventItem();
28 }; // end class Win32NetMontior::EventItem
29
30 class EventList : public ProtoListTemplate<EventItem> {};
31 EventList event_list;
32 EventList event_pool;
33
34 typedef CRITICAL_SECTION Mutex;
35 Mutex lock;
36
Init(Mutex & m)37 static void Init(Mutex& m) {InitializeCriticalSection(&m);}
Destroy(Mutex & m)38 static void Destroy(Mutex& m) {DeleteCriticalSection(&m);}
Lock(Mutex & m)39 static void Lock(Mutex& m) {EnterCriticalSection(&m);}
Unlock(Mutex & m)40 static void Unlock(Mutex& m) {LeaveCriticalSection(&m);}
41
42 HANDLE notification_handle; // handle to subsequently stop notifications
43
44 }; // end class Win32NetMonitor
45
46 /// This is the implementation of the ProtoNet::Monitor::Create()
47 /// static method (our win32-specific factory)
Create()48 ProtoNet::Monitor* ProtoNet::Monitor::Create()
49 {
50 return static_cast<ProtoNet::Monitor*>(new Win32NetMonitor);
51 } // end ProtoNet::Monitor::Create()
52
Win32NetMonitor()53 Win32NetMonitor::Win32NetMonitor()
54 {
55 Init(lock);
56 }
57
~Win32NetMonitor()58 Win32NetMonitor::~Win32NetMonitor()
59 {
60 }
61
62 // Static callback function for NotifyIpInterfaceChange API
IpInterfaceChangeCallback(PVOID callerContext,PMIB_IPINTERFACE_ROW row,MIB_NOTIFICATION_TYPE notificationType)63 static void WINAPI IpInterfaceChangeCallback(PVOID callerContext,
64 PMIB_IPINTERFACE_ROW row,
65 MIB_NOTIFICATION_TYPE notificationType)
66 {
67 Win32NetMonitor* monitor = (Win32NetMonitor*)callerContext;
68 if (!monitor)
69 {
70 PLOG(PL_ERROR,"IpInterfaceChangeCallback() Error: No callerContext.\n");
71 return;
72 }
73
74 if (row)
75 {
76 // Get complete information for MIP_IPINTERFACE_ROW
77 GetIpInterfaceEntry(row);
78 // Add an event to our list for the notification
79 if (!monitor->GetEvent(row,notificationType))
80 {
81 PLOG(PL_ERROR,"MonitorEventHandler() GetEvent error\n");
82 return;
83 }
84
85 }
86 if (!SetEvent(monitor->GetEventHandle()))
87 PLOG(PL_ERROR,"win32Net::MonitorEventHandler() Error setting event handle.\n");
88 }
89
Open()90 bool Win32NetMonitor::Open()
91 {
92 // Not using a manual reset event?
93 if (NULL == (input_handle = CreateEvent(NULL,FALSE,FALSE,NULL)))
94 {
95 input_handle = INVALID_HANDLE_VALUE;
96 PLOG(PL_ERROR,"Win32Monitor::Open() CreateEvent(event_handle) error: %s\n", ::GetErrorString());
97 Close();
98 return false;
99 }
100 // Initiate notifications ...
101 notification_handle = NULL;
102 if (!NotifyIpInterfaceChange(
103 AF_UNSPEC, // AF_INET
104 (PIPINTERFACE_CHANGE_CALLBACK)IpInterfaceChangeCallback,
105 this,
106 false, // initialNofification
107 ¬ification_handle) == NO_ERROR)
108 {
109 PLOG(PL_ERROR,"Win32NetMonitor::Open() NotifyIpInterfaceChange failed\n");
110 return false;
111 }
112
113 if (!ProtoNet::Monitor::Open())
114 {
115 Close();
116 return false;
117 }
118
119 return true;
120 }
121
Close()122 void Win32NetMonitor::Close()
123 {
124 if (IsOpen())
125 {
126 ProtoNet::Monitor::Close();
127 input_handle = INVALID_HANDLE;
128 }
129 if (notification_handle != INVALID_HANDLE)
130 CancelMibChangeNotify2(notification_handle);
131
132 event_list.Destroy();
133 event_pool.Destroy();
134
135 Unlock(lock);
136 Destroy(lock);
137 }
138
GetNotificationType(int type)139 const char* Win32NetMonitor::GetNotificationType(int type)
140 {
141 static const char* names[] = {
142 "ParameterNotification",
143 "AddInstance",
144 "DeleteInstance",
145 "InitialNotification"
146 };
147
148 const char* name = "";
149 if (type >=0 && type < sizeof(names)) {
150 name = names[type];
151 }
152 return name;
153 }
GetNextEvent(Event & theEvent)154 bool Win32NetMonitor::GetNextEvent(Event& theEvent)
155 {
156 // 0) Initialize event instance
157 theEvent.SetType(Event::UNKNOWN_EVENT);
158 theEvent.SetInterfaceIndex(0);
159 theEvent.AccessAddress().Invalidate();
160
161 // 1) Get next event from list
162 Lock(lock);
163 EventItem* eventItem = event_list.RemoveHead();
164 if (eventItem == NULL)
165 {
166 Unlock(lock);
167 theEvent.SetType(Event::NULL_EVENT);
168 return true;
169 }
170 theEvent = static_cast<Event&>(*eventItem);
171 event_pool.Append(*eventItem);
172 Unlock(lock);
173 return true;
174 }
175
GetEvent(PMIB_IPINTERFACE_ROW row,MIB_NOTIFICATION_TYPE notificationType)176 bool Win32NetMonitor::GetEvent(PMIB_IPINTERFACE_ROW row,
177 MIB_NOTIFICATION_TYPE notificationType)
178 {
179 EventItem* eventItem = event_pool.RemoveHead();
180 if (NULL == eventItem) eventItem = new EventItem();
181 if (NULL == eventItem)
182 {
183 PLOG(PL_ERROR,"Win32NetMonitor::GetEvent() new EventItem error: %s\n", GetErrorString());
184 return false;
185 }
186
187 eventItem->SetInterfaceIndex(row->InterfaceIndex);
188
189 switch (notificationType)
190 {
191 case 0:
192 //eventItem->SetType(Event::IFACE_STATE);
193 // not interested in windows state changes at the moment
194 eventItem->SetType(Event::UNKNOWN_EVENT);
195 break;
196 case 1:
197 eventItem->SetType(Event::IFACE_UP);
198 break;
199 case 2:
200 eventItem->SetType(Event::IFACE_DOWN);
201 break;
202 case 3:
203 eventItem->SetType(Event::UNKNOWN_EVENT);
204 break;
205 default:
206 eventItem->SetType(Event::UNKNOWN_EVENT);
207 PLOG(PL_ERROR,"Win32NetMonitor::GetEvent() warning: unhandled network event: %d\n",notificationType);
208 break;
209 }
210
211
212 // Iterate through addresses looking for our interface index
213 ULONG bufferSize = 0;
214 ULONG index = 0;
215 if (ERROR_INSUFFICIENT_BUFFER == GetIpAddrTable(NULL, &bufferSize, FALSE))
216 {
217 char* tableBuffer = new char[bufferSize];
218 if (NULL == tableBuffer)
219 {
220 PLOG(PL_ERROR, "Win32NetMonitor::GetEvent() new tableBuffer error: %s\n", ::GetErrorString());
221 return false;
222 }
223 MIB_IPADDRTABLE* addrTable = (MIB_IPADDRTABLE*)tableBuffer;
224 if (ERROR_SUCCESS == GetIpAddrTable(addrTable, &bufferSize, FALSE))
225 {
226 for (DWORD i = 0; i < addrTable->dwNumEntries; i++)
227 {
228
229 MIB_IPADDRROW* entry = &(addrTable->table[i]);
230 if (entry->dwIndex == row->InterfaceIndex)
231 {
232 if (row->Family == AF_INET)
233 eventItem->AccessAddress().SetRawHostAddress(ProtoAddress::IPv4, (char*)&entry->dwAddr,4);
234 else
235 eventItem->AccessAddress().SetRawHostAddress(ProtoAddress::IPv6, (char*)&entry->dwAddr,16);
236 // TBD Ignore link local addr new/delete events?
237
238 }
239
240 }
241
242 }
243 }
244 else
245 {
246 PLOG(PL_WARN, "Win32NetMonitor::GetEvent(%u) warning GetIpAddrTable() error: %s\n", row->InterfaceIndex, GetErrorString());
247 }
248
249
250 Lock(lock);
251 event_list.Append(*eventItem);
252 Unlock(lock);
253 return true;
254
255 } // end Win32NetMonitor::GetNextEvent();
256
EventItem()257 Win32NetMonitor::EventItem::EventItem()
258 {
259 }
260
~EventItem()261 Win32NetMonitor::EventItem::~EventItem()
262 {
263 }
264
265 #endif // (WINVER >= 0x00600)
266