Microsoft Internet Security and Acceleration Server 2000

IFWXDataFilter

IFWXDataFilter is the interface for a data filter. The data filter functions as the Firewall service's data pump and performs the data filtering.

Notes to Implementers

Filters that examine data require a data pump to pass data through the connection.

There are two approaches to creating data filter instances:

  1. Implement IFWXConnection::AttachDataFilter to create and attach a data filter by adding code to the session filter. The session filter will then attach a data filter instance to the connection whenever the registered event occurs.
  2. Provide an object that implements the standard COM IClassFactory interface. An example of this approach is provided in How to Create an Application Filter.

Remarks

Implement the IFWXConnection interface methods (implemented by the Firewall service) to attach the data pump, and IFWXSessionFilter::FwxFirewallEventHandler to receive information about the event. Through the IFWXDataFilter::SetSockets method, the Firewall service provides socket interfaces to the data filter for the sockets on the internal and external computers. The Firewall service calls the IFWXDataFilter::Detach method when the connection to the sockets is no longer needed. When IFWXDataFilter::Detach is called you must code a release to the sockets so that the system can delete the socket objects.

The Firewall calls IFWXIOCompletion::CompleteAsyncIO when the I/O operation is completed. The data filter object inherits the CompleteAsyncIO method from IFWXIOCompletion. The data is then available for the data pump and filter.

Handling Data Received from Another Protocol

When the primary port of a protocol lies within the dynamic port range (1024 – 5000), a filter for that protocol will receive a firewall event if that port is selected for another protocol's secondary connection. However, when the data stream through that secondary connection does not comply with the filter's expected protocol, the filter must be able to handle this situation gracefully.

The proper approach is to design a filter to revert to a simple data pump if the data stream is not the expected protocol. Note that it is possible in this way for data that would have been rejected at the primary port to pass through the filter at the secondary port.

ISA Samples that Use IFWXDataFilter Methods

The method IFWXDataFilter::SetSockets is used in the following sample filters provided with ISA:

The method IFWXDataFilter::Detach is used in the following samples provided with ISA:

Example Code

Standard Declaration of Data Filter Object

//Note that IFWXIOCompletion::CompleteAsyncIO is made
//public in this declaration, and must be implemented 
//by this object.
class ATL_NO_VTABLE CDumpData :
	public CComObjectRootEx<CComMultiThreadModel>,
	public IFWXDataFilter,
	public IFWXIOCompletion
{

IFWXDataFilter::SetSockets

STDMETHODIMP
//DumpData is the name of object that implements
//IFWXFilter in this example
CDumpData::SetSockets(
	IN   IFWXSocket *piInternalSocket,
	IN   IFWXSocket *piExternalSocket,
	IN   IFWXConnection *piConnection,
	IN   IUnknown *punkFilterContext
	)
{
	// Using Lock/Unlock, all access to the sockets is protected 
	// because Detach could be called at any time.
	Lock();
	m_spInternalSocket = piInternalSocket;
	m_spExternalSocket = piExternalSocket;
	Unlock();
	//ReadFirstBuffer is a function that waits for
	//data on both sockets.
	return ReadFirstBuffers();
}

The variables m_spInternalSocket and m_spExternalSocket are defined as follows:

	CComPtr<IFWXSocket> spExternalSocket = m_spExternalSocket;
	CComPtr<IFWXSocket> spInternalSocket = m_spInternalSocket;

Simple Implementation of CompleteAsyncIO

STDMETHODIMP
//DumpData is the name of object in this example
CDumpData::CompleteAsyncIO(
	IN BOOL	fSuccess,
	IN DWORD	 Win32ErrorCode,
	IN IFWXIOBuffer *pIOBuffer,
	IN DWORD	 dwUserData,
	IN PSOCKADDR From,
	IN INT	 FromLen
	)
{
	DWORD NumberOfBytesTransfered = 0;
	PBYTE pbBuffer;
	BOOL  fShutdown;
	char *pszReadSource;
	IFWXSocket *piSocket;
 
	// Set the target socket for the buffer.
	if (dwUserData == ReadFromInternal) {
		piSocket   = GetExternalSocket();
		pszReadSource = "Internal";
} else {
		piSocket = GetInternalSocket();
		pszReadSource = "External";
}
 
	if (fSuccess && pIOBuffer) {
		pIOBuffer->GetBufferAndSize(&pbBuffer, 
								&NumberOfBytesTransfered);
 
 
	if (piSocket == NULL) {
		return S_OK; 				// Detach was called
}
//This particular filter just receives and sends the data.
//This is the point in the code where a filter's functionality
//should be inserted.
	// Send the buffer
	if (!fSuccess || NumberOfBytesTransfered == 0) {
		fShutdown = TRUE;
		piSocket->Shutdown(!fSuccess);
} 
		else {
		fShutdown = FALSE;
		if (From == NULL) {
			piSocket->Send(pIOBuffer, NULL, 0);
	} else {
			piSocket->SendTo(pIOBuffer, From, FromLen, NULL, 0);
	}
}
 
	piSocket->Release();
 
	if (fShutdown) {
		return S_OK;
}
 
	// Read another buffer
	piSocket = dwUserData == ReadFromInternal ?
		GetInternalSocket() : GetExternalSocket();
 
	if (piSocket == NULL) {
		return S_OK;
}
 
	piSocket->Recv(NULL, this, dwUserData);
 
	piSocket->Release();
 
	return S_OK;
}