/*
  Linux DNS client library implementation

  Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
  Copyright (C) 2006 Gerald Carter <jerry@samba.org>

     ** NOTE! The following LGPL license applies to the libaddns
     ** library. This does NOT imply that all of Samba is released
     ** under the LGPL

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
  02110-1301  USA
*/

#include "dns.h"
#include <sys/time.h>

/********************************************************************
********************************************************************/

static DNS_ERROR DNSTCPOpen( char *nameserver, HANDLE * phDNSServer )
{
	DNS_ERROR dwError = ERROR_DNS_INVALID_PARAMETER;
	int sockServer;
	unsigned long ulAddress;
	struct hostent *pHost;
	struct sockaddr_in s_in;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;

	if ( (pDNSContext = TALLOC_P( NULL, DNS_CONNECTION_CONTEXT )) == NULL ) {
		return ERROR_DNS_NO_MEMORY;
	}

	if ( (ulAddress = inet_addr( nameserver )) == INADDR_NONE ) {
		if ( (pHost = gethostbyname( nameserver )) == NULL ) {
			dwError = ERROR_DNS_INVALID_NAME_SERVER;
			BAIL_ON_DNS_ERROR( dwError );
		}
		memcpy( &ulAddress, pHost->h_addr, pHost->h_length );
	}

	if ( (sockServer = socket( PF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ) {
		dwError = ERROR_DNS_NO_MEMORY;
		BAIL_ON_DNS_ERROR( dwError );
	}

	s_in.sin_family = AF_INET;
	s_in.sin_addr.s_addr = ulAddress;
	s_in.sin_port = htons( DNS_TCP_PORT );

	if ( (connect( sockServer, (struct sockaddr*)&s_in, sizeof( s_in ))) == SOCKET_ERROR ) {
		dwError = ERROR_DNS_CONNECTION_FAILED;
		BAIL_ON_DNS_ERROR( dwError );
	}
		
	pDNSContext->s = sockServer;
	pDNSContext->hType = DNS_TCP;

	*phDNSServer = ( HANDLE ) pDNSContext;

	dwError = ERROR_DNS_SUCCESS;

	return dwError;

error:
	TALLOC_FREE( pDNSContext );
	*phDNSServer = ( HANDLE ) NULL;

	return dwError;
}

/********************************************************************
********************************************************************/

static DNS_ERROR DNSUDPOpen( char *nameserver, HANDLE * phDNSServer )
{
	DNS_ERROR dwError = ERROR_DNS_INVALID_PARAMETER;
	int SendSocket;
	unsigned long ulAddress;
	struct hostent *pHost;
	struct sockaddr_in RecvAddr;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;

	if ( (pDNSContext = TALLOC_P( NULL, DNS_CONNECTION_CONTEXT )) == NULL ) {
		return ERROR_DNS_NO_MEMORY;
	}

	if ( (ulAddress = inet_addr( nameserver )) == INADDR_NONE ) {
		if ( (pHost = gethostbyname( nameserver )) == NULL ) {
			dwError = ERROR_DNS_INVALID_NAME_SERVER;
			BAIL_ON_DNS_ERROR( dwError );
		}
		memcpy( &ulAddress, pHost->h_addr, pHost->h_length );
	}
	
	/* Create a socket for sending data */

	SendSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

	/* Set up the RecvAddr structure with the IP address of
	   the receiver (in this example case "123.456.789.1")
	   and the specified port number. */

	RecvAddr.sin_family = AF_INET;
	RecvAddr.sin_port = htons( DNS_UDP_PORT );
	RecvAddr.sin_addr.s_addr = ulAddress;

	pDNSContext->s = SendSocket;
	pDNSContext->hType = DNS_UDP;
	memcpy( &pDNSContext->RecvAddr, &RecvAddr, sizeof( struct sockaddr_in ) );

	*phDNSServer = ( HANDLE ) pDNSContext;

	dwError = ERROR_DNS_SUCCESS;

	return dwError;

error:
	TALLOC_FREE( pDNSContext );
	*phDNSServer = ( HANDLE ) NULL;

	return dwError;
}

/********************************************************************
********************************************************************/

DNS_ERROR DNSOpen( char *nameserver, int32 dwType, HANDLE * phDNSServer )
{
	switch ( dwType ) {
	case DNS_TCP:
		return DNSTCPOpen( nameserver, phDNSServer );
	case DNS_UDP:
		return DNSUDPOpen( nameserver, phDNSServer );
	}
	
	return ERROR_DNS_INVALID_PARAMETER;
}

/********************************************************************
********************************************************************/

static int32 DNSSendTCPRequest( HANDLE hDNSHandle,
		   uint8 * pDNSSendBuffer,
		   int32 dwBufferSize, int32 * pdwBytesSent )
{
	int32 dwError = 0;
	int32 dwBytesSent = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;


	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;

	dwBytesSent = send( pDNSContext->s, pDNSSendBuffer, dwBufferSize, 0 );
	if ( dwBytesSent == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	}

	*pdwBytesSent = dwBytesSent;

	return dwError;

      error:
	*pdwBytesSent = 0;
	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSSendUDPRequest( HANDLE hDNSHandle,
		   uint8 * pDNSSendBuffer,
		   int32 dwBufferSize, int32 * pdwBytesSent )
{
	int32 dwError = 0;
	int32 dwBytesSent = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;

	dwBytesSent = sendto( pDNSContext->s,
			      pDNSSendBuffer,
			      dwBufferSize,
			      0,
			      ( struct sockaddr * ) & pDNSContext->RecvAddr,
			      sizeof( pDNSContext->RecvAddr )
		 );
	if ( dwBytesSent == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	} else {
		*pdwBytesSent = dwBytesSent;
	}

	return dwError;

      error:
	*pdwBytesSent = 0;
	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSSelect( HANDLE hDNSHandle )
{
	int32 dwError = 0;
	fd_set rfds;
	struct timeval tv;
	int32 dwNumSockets = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;
	FD_ZERO( &rfds );
	FD_SET( pDNSContext->s, &rfds );

	tv.tv_sec = 10;
	tv.tv_usec = 0;
	dwNumSockets = select( pDNSContext->s + 1, &rfds, NULL, NULL, &tv );
	if ( dwNumSockets == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	}

	if ( !dwNumSockets ) {
#ifndef WIN32
		dwError = ETIMEDOUT;
#else
		dwError = WSAETIMEDOUT;
#endif
	}

      error:

	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSTCPReceiveBufferContext( HANDLE hDNSHandle,
			    HANDLE hDNSRecvBuffer, int32 * pdwBytesRead )
{
	int32 dwError = 0;
	int32 dwRead = 0;
	int16 wBytesToRead = 0;
	int16 wnBytesToRead = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;
	DNS_RECEIVEBUFFER_CONTEXT *pDNSRecvContext = NULL;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;
	pDNSRecvContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hDNSRecvBuffer;

	dwError = DNSSelect( hDNSHandle );
	BAIL_ON_ERROR( dwError );

	dwRead = recv( pDNSContext->s, ( char * ) &wnBytesToRead,
		       sizeof( int16 ), 0 );
	if ( dwRead == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	}

	wBytesToRead = ntohs( wnBytesToRead );

	dwError = DNSSelect( hDNSHandle );
	BAIL_ON_ERROR( dwError );

	dwRead = recv( pDNSContext->s,
		       ( char * ) pDNSRecvContext->pRecvBuffer, wBytesToRead,
		       0 );
	if ( dwRead == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	}

	pDNSRecvContext->dwBytesRecvd = dwRead;

	*pdwBytesRead = ( int32 ) dwRead;

	return dwError;

      error:

	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSUDPReceiveBufferContext( HANDLE hDNSHandle,
			    HANDLE hDNSRecvBuffer, int32 * pdwBytesRead )
{
	int32 dwError = 0;
	int32 dwRead = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;
	DNS_RECEIVEBUFFER_CONTEXT *pDNSRecvContext = NULL;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;
	pDNSRecvContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hDNSRecvBuffer;

	dwError = DNSSelect( hDNSHandle );
	BAIL_ON_ERROR( dwError );

	dwRead = recv( pDNSContext->s,
		       ( char * ) pDNSRecvContext->pRecvBuffer, 512, 0 );
	if ( dwRead == SOCKET_ERROR ) {
		dwError = WSAGetLastError(  );
		BAIL_ON_ERROR( dwError );
	}

	pDNSRecvContext->dwBytesRecvd = dwRead;

	*pdwBytesRead = ( int32 ) dwRead;

      error:

	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSReceiveBufferContext( HANDLE hDNSHandle,
			 HANDLE hDNSRecvBuffer, int32 * pdwBytesRead )
{
	int32 dwError = 0;
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSHandle;

	switch ( pDNSContext->hType ) {
	case DNS_TCP:
		dwError =
			DNSTCPReceiveBufferContext( hDNSHandle,
						    hDNSRecvBuffer,
						    pdwBytesRead );
		break;
	case DNS_UDP:
		dwError =
			DNSUDPReceiveBufferContext( hDNSHandle,
						    hDNSRecvBuffer,
						    pdwBytesRead );
		break;
	}
	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSCreateSendBuffer( HANDLE * phDNSSendBuffer )
{
	int32 dwError = 0;
	DNS_SENDBUFFER_CONTEXT *pDNSContext = NULL;
	uint8 *pSendBuffer = NULL;

	dwError = DNSAllocateMemory( sizeof( DNS_SENDBUFFER_CONTEXT ),
				     ( void * ) &pDNSContext );
	BAIL_ON_ERROR( dwError );

	dwError =
		DNSAllocateMemory( SENDBUFFER_SIZE,
				   ( void * ) &pSendBuffer );
	BAIL_ON_ERROR( dwError );

	pDNSContext->pSendBuffer = pSendBuffer;
	pDNSContext->dwBufferSize = SENDBUFFER_SIZE;

	/* We will offset into the buffer by 2 bytes
	   If we are doing a TCP write; we will fill in these
	   two bytes and send + 2 bytes
	   If we are doing a UDP write; we will start our send
	   +2 bytes and only send dwWritten; */

	pDNSContext->dwBufferOffset += 2;

	*phDNSSendBuffer = ( HANDLE ) pDNSContext;

	return dwError;

      error:

	if ( pSendBuffer ) {
		DNSFreeMemory( pSendBuffer );
	}
	if ( pDNSContext ) {
		DNSFreeMemory( pDNSContext );
	}
	*phDNSSendBuffer = ( HANDLE ) NULL;

	return dwError;
}


/********************************************************************
********************************************************************/

int32 DNSMarshallBuffer( HANDLE hDNSSendBuffer,
		   uint8 * pDNSSendBuffer,
		   int32 dwBufferSize, int32 * pdwBytesWritten )
{
	int32 dwError = 0;
	uint8 *pTemp = NULL;
	DNS_SENDBUFFER_CONTEXT *pDNSContext = NULL;

/* BugBug - we need to check for amount of space remaining in the
SendBuffer Context - if its insufficent, we want to realloc the 
Buffer and copy the context; Right now the assumption is we have a big
enough buffer */

	pDNSContext = ( DNS_SENDBUFFER_CONTEXT * ) hDNSSendBuffer;

	pTemp = pDNSContext->pSendBuffer + pDNSContext->dwBufferOffset;

	memcpy( pTemp, pDNSSendBuffer, dwBufferSize );

	pDNSContext->dwBytesWritten += dwBufferSize;
	pDNSContext->dwBufferOffset += dwBufferSize;

	*pdwBytesWritten = dwBufferSize;

	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSTCPSendBufferContext( HANDLE hDNSServer,
			 HANDLE hSendBuffer, int32 * pdwBytesSent )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;
	int32 dwError = 0;
	int16 wBytesWritten = 0;
	int16 wnBytesWritten = 0;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;

	wBytesWritten = ( int16 ) pSendBufferContext->dwBytesWritten;
	wnBytesWritten = htons( wBytesWritten );

	memcpy( pSendBufferContext->pSendBuffer, &wnBytesWritten,
		sizeof( int16 ) );

	dwError = DNSSendTCPRequest( hDNSServer,
				     pSendBufferContext->pSendBuffer,
				     pSendBufferContext->dwBytesWritten + 2,
				     pdwBytesSent );
	BAIL_ON_ERROR( dwError );

      error:

	return dwError;
}

/********************************************************************
********************************************************************/

static int32 DNSUDPSendBufferContext( HANDLE hDNSServer,
			 HANDLE hSendBuffer, int32 * pdwBytesSent )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;
	int32 dwError = 0;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;

	/* Now remember to send 2 bytes ahead of pSendBuffer; because
	   we ignore the 2 bytes size field. */

	dwError = DNSSendUDPRequest( hDNSServer,
				     pSendBufferContext->pSendBuffer + 2,
				     pSendBufferContext->dwBytesWritten,
				     pdwBytesSent );
	BAIL_ON_ERROR( dwError );

      error:

	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSSendBufferContext( HANDLE hDNSServer,
		      HANDLE hSendBuffer, int32 * pdwBytesSent )
{
	DNS_CONNECTION_CONTEXT *pDNSContext = NULL;
	int32 dwError = 0;

	pDNSContext = ( DNS_CONNECTION_CONTEXT * ) hDNSServer;

	switch ( pDNSContext->hType ) {
	case DNS_TCP:
		dwError = DNSTCPSendBufferContext( hDNSServer,
						   hSendBuffer,
						   pdwBytesSent );
		BAIL_ON_ERROR( dwError );
		break;

	case DNS_UDP:
		dwError = DNSUDPSendBufferContext( hDNSServer,
						   hSendBuffer,
						   pdwBytesSent );
		BAIL_ON_ERROR( dwError );
		break;
	}
      error:

	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSDumpSendBufferContext( HANDLE hSendBuffer )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;
	int32 dwError = 0;
	int32 dwCurLine = 0;
	int32 i = 0;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;
	printf( "\n" );
	printf( "Buffer Size is: %d\n", pSendBufferContext->dwBytesWritten );
	while ( i < pSendBufferContext->dwBytesWritten ) {
		if ( ( i / 16 ) > dwCurLine ) {
			printf( "\n" );
			dwCurLine++;
		}
		if ( ( i % 8 ) == 0 ) {
			printf( "  " );
		}
		printf( "%.2x ", pSendBufferContext->pSendBuffer[i] );
		i++;
	}
	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSDumpRecvBufferContext( HANDLE hRecvBuffer )
{
	DNS_RECEIVEBUFFER_CONTEXT *pRecvBufferContext = NULL;
	int32 dwError = 0;
	int32 dwCurLine = 0;
	int32 i = 0;

	pRecvBufferContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hRecvBuffer;

	printf( "\n" );
	printf( "Buffer Size is: %d\n", pRecvBufferContext->dwBytesRecvd );

	while ( i < pRecvBufferContext->dwBytesRecvd ) {
		if ( ( i / 16 ) > dwCurLine ) {
			printf( "\n" );
			dwCurLine++;
		}
		if ( ( i % 8 ) == 0 ) {
			printf( "  " );
		}
		printf( "%.2x ", pRecvBufferContext->pRecvBuffer[i] );
		i++;
	}
	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSCreateReceiveBuffer( HANDLE * phDNSRecvBuffer )
{
	int32 dwError = 0;
	DNS_RECEIVEBUFFER_CONTEXT *pDNSContext = NULL;
	uint8 *pRecvBuffer = NULL;

	dwError = DNSAllocateMemory( sizeof( DNS_RECEIVEBUFFER_CONTEXT ),
				     ( void * ) &pDNSContext );
	BAIL_ON_ERROR( dwError );

	dwError =
		DNSAllocateMemory( RECVBUFFER_SIZE,
				   ( void * ) &pRecvBuffer );
	BAIL_ON_ERROR( dwError );

	pDNSContext->pRecvBuffer = pRecvBuffer;
	pDNSContext->dwBufferSize = RECVBUFFER_SIZE;

	*phDNSRecvBuffer = ( HANDLE ) pDNSContext;

	return dwError;

      error:

	if ( pRecvBuffer ) {
		DNSFreeMemory( pRecvBuffer );
	}
	if ( pDNSContext ) {
		DNSFreeMemory( pDNSContext );
	}
	*phDNSRecvBuffer = ( HANDLE ) NULL;

	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSUnmarshallBuffer( HANDLE hDNSRecvBuffer,
		     uint8 * pDNSRecvBuffer,
		     int32 dwBufferSize, int32 * pdwBytesRead )
{
	int32 dwError = 0;
	uint8 *pTemp = NULL;
	DNS_RECEIVEBUFFER_CONTEXT *pDNSContext = NULL;

/* BugBug - we need to check for amount of space remaining in the
SendBuffer Context - if its insufficent, we want to realloc the 
Buffer and copy the context; Right now the assumption is we have a big
enough buffer */

	pDNSContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hDNSRecvBuffer;

	pTemp = pDNSContext->pRecvBuffer + pDNSContext->dwBytesRead;

	memcpy( pDNSRecvBuffer, pTemp, dwBufferSize );

	pDNSContext->dwBytesRead += dwBufferSize;

	*pdwBytesRead = dwBufferSize;

	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSUnmarshallDomainNameAtOffset( HANDLE hRecvBuffer,
				 int16 wOffset,
				 DNS_DOMAIN_NAME ** ppDomainName )
{
	int32 dwError = 0;
	DNS_DOMAIN_LABEL *pLabel = NULL;
	DNS_DOMAIN_LABEL *pLabelList = NULL;
	DNS_DOMAIN_NAME *pDomainName = NULL;
	char *pszLabel = NULL;
	char szLabel[65];
	uint8 uLen = 0;
	int32 dwCurrent = 0;
	DNS_RECEIVEBUFFER_CONTEXT *pRecvContext = NULL;

	pRecvContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hRecvBuffer;
	dwCurrent = wOffset;

	while ( 1 ) {

		memcpy( &uLen, pRecvContext->pRecvBuffer + dwCurrent,
			sizeof( char ) );
		dwCurrent++;

		if ( uLen == 0 ) {
			break;
		}

		memset( szLabel, 0, 65 );
		memcpy( szLabel, pRecvContext->pRecvBuffer + dwCurrent,
			uLen );
		dwCurrent += uLen;

		dwError = DNSAllocateString( szLabel, &pszLabel );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSAllocateMemory( sizeof( DNS_DOMAIN_LABEL ),
					   ( void * ) &pLabel );
		BAIL_ON_ERROR( dwError );

		pLabel->pszLabel = pszLabel;
		dwError = DNSAppendLabel( pLabelList, pLabel, &pLabelList );
		BAIL_ON_ERROR( dwError );
	}

	dwError =
		DNSAllocateMemory( sizeof( DNS_DOMAIN_NAME ),
				   ( void * ) &pDomainName );
	BAIL_ON_ERROR( dwError );
	pDomainName->pLabelList = pLabelList;

	*ppDomainName = pDomainName;

	return dwError;

      error:

	*ppDomainName = NULL;
	return dwError;
}

/********************************************************************
********************************************************************/

int32 DNSReceiveBufferMoveBackIndex( HANDLE hRecvBuffer, int16 wOffset )
{
	int32 dwError = 0;
	DNS_RECEIVEBUFFER_CONTEXT *pDNSContext = NULL;

	pDNSContext = ( DNS_RECEIVEBUFFER_CONTEXT * ) hRecvBuffer;
	pDNSContext->dwBytesRead -= wOffset;

	return dwError;
}

/********************************************************************
********************************************************************/

void DNSFreeSendBufferContext( HANDLE hSendBuffer )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;

	if ( pSendBufferContext && pSendBufferContext->pSendBuffer ) {
		DNSFreeMemory( pSendBufferContext->pSendBuffer );
	}
	if ( pSendBufferContext ) {
		DNSFreeMemory( pSendBufferContext );
	}
}

/********************************************************************
********************************************************************/

int32 DNSGetSendBufferContextSize( HANDLE hSendBuffer )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;

	return ( pSendBufferContext->dwBytesWritten );

}

/********************************************************************
********************************************************************/

uint8 *DNSGetSendBufferContextBuffer( HANDLE hSendBuffer )
{
	DNS_SENDBUFFER_CONTEXT *pSendBufferContext = NULL;

	pSendBufferContext = ( DNS_SENDBUFFER_CONTEXT * ) hSendBuffer;

	return ( pSendBufferContext->pSendBuffer );
}