Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

282 lines
6.3 KiB

#include "stdafx.h"
#include "caddress.h"
#if !defined( BITS_V12_ON_NT4 )
#include "caddress.tmh"
#endif
CIpAddressMonitor::CIpAddressMonitor() :
m_AddressCount( -1 ),
m_ListenSocket( INVALID_SOCKET ),
m_CallbackFn ( NULL ),
m_CallbackArg( NULL )
{
m_Overlapped.Internal = 0;
WSADATA data;
DWORD s = WSAStartup( MAKEWORD( 2, 0 ), &data );
if (s)
{
THROW_HRESULT( HRESULT_FROM_WIN32( s ));
}
THROW_HRESULT( CreateListenSocket() );
}
CIpAddressMonitor::~CIpAddressMonitor()
{
CancelListen();
if (m_ListenSocket != INVALID_SOCKET)
{
closesocket( m_ListenSocket );
}
WSACleanup();
}
HRESULT
CIpAddressMonitor::CreateListenSocket()
{
//
// Create an overlapped socket.
//
m_ListenSocket = WSASocket( AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL, // no explicit protocol info
NULL, // no group
WSA_FLAG_OVERLAPPED
);
if (m_ListenSocket == INVALID_SOCKET)
{
return HRESULT_FROM_WIN32( WSAGetLastError() );
}
return S_OK;
}
bool
CIpAddressMonitor::IsListening()
{
return (m_Overlapped.Internal == STATUS_PENDING);
}
long
CIpAddressMonitor::GetAddressCount()
{
if (m_AddressCount == -1)
{
UpdateAddressCount();
// if this failed, m_AddressCount may still be -1.
}
return m_AddressCount;
}
HRESULT
CIpAddressMonitor::Listen(
LISTEN_CALLBACK_FN fn,
PVOID arg
)
{
LogInfo("begin listen");
m_Mutex.Enter();
//
// Only one listen at a time.
//
if (IsListening())
{
m_Mutex.Leave();
LogInfo("already listening");
return S_FALSE;
}
if (m_ListenSocket == INVALID_SOCKET)
{
HRESULT hr = CreateListenSocket();
if (FAILED(hr))
{
m_Mutex.Leave();
LogInfo("failed %x", hr);
return hr;
}
}
//
// Listen for address list changes.
//
DWORD bytes;
if (SOCKET_ERROR == WSAIoctl( m_ListenSocket,
SIO_ADDRESS_LIST_CHANGE,
NULL, // no in buffer
0, // no in buffer
NULL, // no out buffer
0, // no out buffer,
&bytes,
&m_Overlapped,
CIpAddressMonitor::ListenCompletionRoutine
))
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
HRESULT HrError = HRESULT_FROM_WIN32( WSAGetLastError() );
m_Mutex.Leave();
LogInfo("failed %x", HrError);
return HrError;
}
}
//
// Note our success.
//
m_CallbackFn = fn;
m_CallbackArg = arg;
m_Mutex.Leave();
LogInfo("end listen");
return S_OK;
}
void CALLBACK
CIpAddressMonitor::ListenCompletionRoutine(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
)
{
CIpAddressMonitor * obj = CONTAINING_RECORD( lpOverlapped, CIpAddressMonitor, m_Overlapped );
LogInfo("completion routine, object %p, err %d", obj, dwError );
if (dwError == 0)
{
obj->m_Mutex.Enter();
obj->UpdateAddressCount();
PVOID arg = obj->m_CallbackArg;
LISTEN_CALLBACK_FN fn = obj->m_CallbackFn;
obj->m_Mutex.Leave();
if (fn)
{
fn( arg );
}
}
}
void
CIpAddressMonitor::CancelListen()
{
LogInfo("begin cancel");
m_Mutex.Enter();
if (!IsListening())
{
m_Mutex.Leave();
LogInfo("no need to cancel");
return;
}
//
// Must wait for the I/O to be completed or aborted, since m_Overlapped
// is written to in both cases.
//
CancelIo( HANDLE(m_ListenSocket) );
long count = 0;
while (m_Overlapped.Internal == STATUS_PENDING)
{
if (0 == (count % 100) )
{
LogInfo("waiting %d times...", count);
}
SleepEx( 1, TRUE );
++count;
}
closesocket( m_ListenSocket );
m_ListenSocket = INVALID_SOCKET;
m_Mutex.Leave();
//
// The overlapped operation is no longer pending, but the APC may still be queued.
// Allow it to run.
//
SleepEx( 1, TRUE );
LogInfo("end cancel");
}
HRESULT
CIpAddressMonitor::UpdateAddressCount()
{
//
// First call gets the required buffer size...
//
DWORD bytes;
WSAIoctl( m_ListenSocket,
SIO_ADDRESS_LIST_QUERY,
NULL, // no in buffer
0, // no in buffer
NULL, // no out buffer
0, // no out buffer,
&bytes,
NULL, // no OVERLAPPED
NULL // no completion routine
);
if (WSAGetLastError() != WSAEFAULT)
{
m_AddressCount = -1;
return HRESULT_FROM_WIN32( WSAGetLastError());
}
auto_ptr<char> Buffer;
try
{
Buffer = auto_ptr<char>( new char[ bytes ] );
}
catch( ComError Error )
{
return Error.Error();
}
SOCKET_ADDRESS_LIST * List = reinterpret_cast<SOCKET_ADDRESS_LIST *>(Buffer.get());
//
// ...second call gets the data.
//
if (SOCKET_ERROR == WSAIoctl( m_ListenSocket,
SIO_ADDRESS_LIST_QUERY,
NULL,
0,
List,
bytes,
&bytes,
NULL,
NULL
))
{
m_AddressCount = -1;
return HRESULT_FROM_WIN32( WSAGetLastError());
}
m_AddressCount = List->iAddressCount;
return S_OK;
}