/*
  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"

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

static int32 DNSUpdateAllocateResponse( DNS_UPDATE_RESPONSE ** ppDNSResponse )
{
	int32 dwError = 0;
	DNS_UPDATE_RESPONSE *pDNSResponse = NULL;

	dwError =
		DNSAllocateMemory( sizeof( DNS_UPDATE_RESPONSE ),
				   ( void * ) &pDNSResponse );
	BAIL_ON_ERROR( dwError );

	*ppDNSResponse = pDNSResponse;

	return dwError;

      error:

	*ppDNSResponse = NULL;

	return dwError;
}

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

static int32 DNSUpdateUnmarshallAdditionalSection( HANDLE hReceiveBuffer,
				      int16 wAdditionals,
				      DNS_RR_RECORD *
				      **pppDNSAdditionalsRRRecords )
{
	int32 dwError = 0;
	int32 i = 0;
	DNS_RR_RECORD *pDNSRRRecord = NULL;
	DNS_RR_RECORD **ppDNSAdditionalRRRecords = NULL;
	DNS_RR_HEADER RRHeader = { 0 };
	DNS_RR_HEADER *pRRHeader = &RRHeader;
	uint8 *pRRData = NULL;
	int32 dwRead = 0;

	dwError = DNSAllocateMemory( wAdditionals * sizeof( DNS_RR_RECORD * ),
				     ( void * ) &ppDNSAdditionalRRRecords );
	BAIL_ON_ERROR( dwError );

	for ( i = 0; i < wAdditionals; i++ ) {

		memset( pRRHeader, 0, sizeof( DNS_RR_HEADER ) );
		dwError = DNSUnmarshallRRHeader( hReceiveBuffer, pRRHeader );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSUnmarshallRData( hReceiveBuffer,
					    pRRHeader->wRDataSize, &pRRData,
					    &dwRead );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSAllocateMemory( sizeof( DNS_RR_RECORD ),
					   ( void * ) &pDNSRRRecord );
		BAIL_ON_ERROR( dwError );

		memcpy( &pDNSRRRecord->RRHeader, pRRHeader,
			sizeof( DNS_RR_HEADER ) );
		pDNSRRRecord->pRData = pRRData;

		*( ppDNSAdditionalRRRecords + i ) = pDNSRRRecord;
	}

      error:

	return dwError;
}

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

static int32 DNSUpdateUnmarshallPRSection( HANDLE hReceiveBuffer,
			      int16 wPRs,
			      DNS_RR_RECORD * **pppDNSPRRRRecords )
{
	int32 dwError = 0;
	int32 i = 0;
	DNS_RR_RECORD *pDNSRRRecord = NULL;
	DNS_RR_RECORD **ppDNSPRRRRecords = NULL;
	DNS_RR_HEADER RRHeader = { 0 };
	DNS_RR_HEADER *pRRHeader = &RRHeader;
	uint8 *pRRData = NULL;
	int32 dwRead = 0;

	dwError = DNSAllocateMemory( wPRs * sizeof( DNS_RR_RECORD * ),
				     ( void * ) &ppDNSPRRRRecords );
	BAIL_ON_ERROR( dwError );

	for ( i = 0; i < wPRs; i++ ) {

		memset( pRRHeader, 0, sizeof( DNS_RR_HEADER ) );
		dwError = DNSUnmarshallRRHeader( hReceiveBuffer, pRRHeader );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSUnmarshallRData( hReceiveBuffer,
					    pRRHeader->wRDataSize, &pRRData,
					    &dwRead );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSAllocateMemory( sizeof( DNS_RR_RECORD ),
					   ( void * ) &pDNSRRRecord );
		BAIL_ON_ERROR( dwError );

		memcpy( &pDNSRRRecord->RRHeader, pRRHeader,
			sizeof( DNS_RR_HEADER ) );
		pDNSRRRecord->pRData = pRRData;

		*( ppDNSPRRRRecords + i ) = pDNSRRRecord;
	}

	*pppDNSPRRRRecords = ppDNSPRRRRecords;

	return dwError;

      error:


	return dwError;
}

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

static int32 DNSUpdateUnmarshallUpdateSection( HANDLE hReceiveBuffer,
				  int16 wUpdates,
				  DNS_RR_RECORD * **pppDNSUpdateRRRecords )
{
	int32 dwError = 0;
	int32 i = 0;
	DNS_RR_RECORD *pDNSRRRecord = NULL;
	DNS_RR_RECORD **ppDNSUpdateRRRecords = NULL;
	DNS_RR_HEADER RRHeader = { 0 };
	DNS_RR_HEADER *pRRHeader = &RRHeader;
	uint8 *pRRData = NULL;
	int32 dwRead = 0;

	dwError = DNSAllocateMemory( wUpdates * sizeof( DNS_RR_RECORD * ),
				     ( void * ) &ppDNSUpdateRRRecords );
	BAIL_ON_ERROR( dwError );

	for ( i = 0; i < wUpdates; i++ ) {

		memset( pRRHeader, 0, sizeof( DNS_RR_HEADER ) );
		dwError = DNSUnmarshallRRHeader( hReceiveBuffer, pRRHeader );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSUnmarshallRData( hReceiveBuffer,
					    pRRHeader->wRDataSize, &pRRData,
					    &dwRead );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSAllocateMemory( sizeof( DNS_RR_RECORD ),
					   ( void * ) &pDNSRRRecord );
		BAIL_ON_ERROR( dwError );

		memcpy( &pDNSRRRecord->RRHeader, pRRHeader,
			sizeof( DNS_RR_HEADER ) );
		pDNSRRRecord->pRData = pRRData;

		*( ppDNSUpdateRRRecords + i ) = pDNSRRRecord;
	}

	*pppDNSUpdateRRRecords = ppDNSUpdateRRRecords;

	return dwError;

      error:

	return dwError;

}

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

static int32 DNSUpdateUnmarshallZoneSection( HANDLE hReceiveBuffer,
				int16 wZones,
				DNS_ZONE_RECORD * **pppDNSZoneRecords )
{
	int32 dwError = 0;
	int32 i = 0;
	int32 dwRead = 0;
	DNS_ZONE_RECORD *pDNSZoneRecord = NULL;
	DNS_ZONE_RECORD **ppDNSZoneRecords = NULL;
	int16 wnZoneClass = 0;
	int16 wnZoneType = 0;


	dwError = DNSAllocateMemory( wZones * sizeof( DNS_ZONE_RECORD * ),
				     ( void * ) &ppDNSZoneRecords );
	BAIL_ON_ERROR( dwError );

	for ( i = 0; i < wZones; i++ ) {

		dwError =
			DNSAllocateMemory( sizeof( DNS_ZONE_RECORD ),
					   ( void * ) &pDNSZoneRecord );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSUnmarshallDomainName( hReceiveBuffer,
						 &pDNSZoneRecord->
						 pDomainName );
		BAIL_ON_ERROR( dwError );

		dwError =
			DNSUnmarshallBuffer( hReceiveBuffer,
					     ( uint8 * ) & wnZoneType,
					     ( int32 ) sizeof( int16 ),
					     &dwRead );
		BAIL_ON_ERROR( dwError );
		pDNSZoneRecord->wZoneType = ntohs( wnZoneType );

		dwError =
			DNSUnmarshallBuffer( hReceiveBuffer,
					     ( uint8 * ) & wnZoneClass,
					     ( int32 ) sizeof( int16 ),
					     &dwRead );
		BAIL_ON_ERROR( dwError );
		pDNSZoneRecord->wZoneClass = ntohs( wnZoneClass );

		*( ppDNSZoneRecords + i ) = pDNSZoneRecord;
	}

	*pppDNSZoneRecords = ppDNSZoneRecords;
	return dwError;

      error:

	return dwError;
}


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

int32 DNSUpdateReceiveUpdateResponse( HANDLE hDNSHandle,
				DNS_UPDATE_RESPONSE ** ppDNSResponse )
{
	DNS_UPDATE_RESPONSE *pDNSResponse = NULL;
	int32 dwError = 0;
	int16 wnIdentification, wIdentification = 0;
	int16 wnParameter, wParameter = 0;
	int16 wnZones, wZones = 0;
	int16 wnPRs, wPRs = 0;
	int16 wnAdditionals, wAdditionals = 0;
	int16 wnUpdates, wUpdates = 0;
	int32 dwRead = 0;
	DNS_RR_RECORD **ppDNSPRRecords = NULL;
	DNS_RR_RECORD **ppDNSAdditionalRecords = NULL;
	DNS_RR_RECORD **ppDNSUpdateRecords = NULL;
	DNS_ZONE_RECORD **ppDNSZoneRecords = NULL;
	HANDLE hRecvBuffer = ( HANDLE ) NULL;

	dwError = DNSCreateReceiveBuffer( &hRecvBuffer );
	BAIL_ON_ERROR( dwError );

	dwError = DNSReceiveBufferContext( hDNSHandle, hRecvBuffer, &dwRead );
	BAIL_ON_ERROR( dwError );

#if 0
	dwError = DNSDumpRecvBufferContext( hRecvBuffer );
	BAIL_ON_ERROR( dwError );
#endif

	dwError =
		DNSUnmarshallBuffer( hRecvBuffer,
				     ( uint8 * ) & wnIdentification,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wIdentification = ntohs( wnIdentification );

	dwError =
		DNSUnmarshallBuffer( hRecvBuffer, ( uint8 * ) & wnParameter,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wParameter = ntohs( wnParameter );


	dwError =
		DNSUnmarshallBuffer( hRecvBuffer, ( uint8 * ) & wnZones,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wZones = ntohs( wnZones );


	dwError =
		DNSUnmarshallBuffer( hRecvBuffer, ( uint8 * ) & wnPRs,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wPRs = ntohs( wnPRs );


	dwError =
		DNSUnmarshallBuffer( hRecvBuffer, ( uint8 * ) & wnUpdates,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wUpdates = ntohs( wnUpdates );

	dwError =
		DNSUnmarshallBuffer( hRecvBuffer, ( uint8 * ) & wnAdditionals,
				     sizeof( int16 ), &dwRead );
	BAIL_ON_ERROR( dwError );
	wAdditionals = ntohs( wnAdditionals );


	if ( wZones ) {
		dwError =
			DNSUpdateUnmarshallZoneSection( hRecvBuffer, wZones,
							&ppDNSZoneRecords );
		BAIL_ON_ERROR( dwError );
	}

	if ( wPRs ) {
		dwError =
			DNSUpdateUnmarshallPRSection( hRecvBuffer, wPRs,
						      &ppDNSPRRecords );
		BAIL_ON_ERROR( dwError );
	}

	if ( wUpdates ) {
		dwError =
			DNSUpdateUnmarshallUpdateSection( hRecvBuffer,
							  wUpdates,
							  &ppDNSUpdateRecords );
		BAIL_ON_ERROR( dwError );
	}

	if ( wAdditionals ) {
		dwError =
			DNSUpdateUnmarshallAdditionalSection( hRecvBuffer,
							      wAdditionals,
							      &ppDNSAdditionalRecords );
		BAIL_ON_ERROR( dwError );
	}

	dwError = DNSUpdateAllocateResponse( &pDNSResponse );
	BAIL_ON_ERROR( dwError );

	pDNSResponse->wIdentification = wIdentification;
	pDNSResponse->wParameter = wParameter;
	pDNSResponse->wZones = wZones;
	pDNSResponse->wPRs = wPRs;
	pDNSResponse->wUpdates = wUpdates;
	pDNSResponse->wAdditionals = wAdditionals;

	pDNSResponse->ppZoneRRSet = ppDNSZoneRecords;
	pDNSResponse->ppPRRRSet = ppDNSPRRecords;
	pDNSResponse->ppUpdateRRSet = ppDNSUpdateRecords;
	pDNSResponse->ppAdditionalRRSet = ppDNSAdditionalRecords;

	*ppDNSResponse = pDNSResponse;


      error:

	return dwError;
}