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 				&notification_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