// DNSSD.cpp : Implementation of CDNSSD

#include "stdafx.h"
#include "DNSSD.h"
#include "DNSSDService.h"
#include "TXTRecord.h"
#include <dns_sd.h>
#include <CommonServices.h>
#include <DebugServices.h>
#include "StringServices.h"


// CDNSSD

STDMETHODIMP CDNSSD::Browse(DNSSDFlags flags, ULONG ifIndex, BSTR regtype, BSTR domain, IBrowseListener* listener, IDNSSDService** browser )
{
	CComObject<CDNSSDService>	*	object		= NULL;
	std::string						regtypeUTF8;
	std::string						domainUTF8;
	DNSServiceRef					sref		= NULL;
	DNSServiceErrorType				err			= 0;
	HRESULT							hr			= 0;
	BOOL							ok;

	// Initialize
	*browser = NULL;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( regtype, regtypeUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( domain, domainUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceBrowse( &sref, flags, ifIndex, regtypeUTF8.c_str(), domainUTF8.c_str(), ( DNSServiceBrowseReply ) &BrowseReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*browser = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::Resolve(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, IResolveListener* listener, IDNSSDService** service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	std::string						serviceNameUTF8;
	std::string						regTypeUTF8;
	std::string						domainUTF8;
	DNSServiceRef					sref			= NULL;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;
	BOOL							ok;

	// Initialize
	*service = NULL;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( regType, regTypeUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( domain, domainUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceResolve( &sref, flags, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), domainUTF8.c_str(), ( DNSServiceResolveReply ) &ResolveReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::EnumerateDomains(DNSSDFlags flags, ULONG ifIndex, IDomainListener *listener, IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	DNSServiceRef					sref			= NULL;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;

	// Initialize
	*service = NULL;

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceEnumerateDomains( &sref, flags, ifIndex, ( DNSServiceDomainEnumReply ) &DomainEnumReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::Register(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, BSTR host, USHORT port, ITXTRecord *record, IRegisterListener *listener, IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	std::string						serviceNameUTF8;
	std::string						regTypeUTF8;
	std::string						domainUTF8;
	std::string						hostUTF8;
	const void					*	txtRecord		= NULL;
	uint16_t						txtLen			= 0;
	DNSServiceRef					sref			= NULL;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;
	BOOL							ok;

	// Initialize
	*service = NULL;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( regType, regTypeUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( domain, domainUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );
	ok = BSTRToUTF8( host, hostUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	if ( record )
	{
		CComObject< CTXTRecord > * realTXTRecord;

		realTXTRecord = ( CComObject< CTXTRecord >* ) record;

		txtRecord	= realTXTRecord->GetBytes();
		txtLen		= realTXTRecord->GetLen();
	}

	err = DNSServiceRegister( &sref, flags, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), domainUTF8.c_str(), hostUTF8.c_str(), port, txtLen, txtRecord, ( DNSServiceRegisterReply ) &RegisterReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::QueryRecord(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, IQueryRecordListener *listener, IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	DNSServiceRef					sref			= NULL;
	std::string						fullNameUTF8;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;
	BOOL							ok;

	// Initialize
	*service = NULL;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( fullname, fullNameUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceQueryRecord( &sref, flags, ifIndex, fullNameUTF8.c_str(), ( uint16_t ) rrtype, ( uint16_t ) rrclass, ( DNSServiceQueryRecordReply ) &QueryRecordReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::GetAddrInfo(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, BSTR hostName, IGetAddrInfoListener *listener, IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	DNSServiceRef					sref			= NULL;
	std::string						hostNameUTF8;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;
	BOOL							ok;

	// Initialize
	*service = NULL;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( hostName, hostNameUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceGetAddrInfo( &sref, flags, ifIndex, addressFamily, hostNameUTF8.c_str(), ( DNSServiceGetAddrInfoReply ) &GetAddrInfoReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::CreateConnection(IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object	= NULL;
	DNSServiceRef					sref	= NULL;
	DNSServiceErrorType				err		= 0;
	HRESULT							hr		= 0;

	// Initialize
	*service = NULL;

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	err = DNSServiceCreateConnection( &sref );
	require_noerr( err, exit );

	object->SetServiceRef( sref );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::NATPortMappingCreate(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, DNSSDProtocol protocol, USHORT internalPort, USHORT externalPort, ULONG ttl, INATPortMappingListener *listener, IDNSSDService **service)
{
	CComObject<CDNSSDService>	*	object			= NULL;
	DNSServiceRef					sref			= NULL;
	DNSServiceProtocol				prot			= 0;
	DNSServiceErrorType				err				= 0;
	HRESULT							hr				= 0;

	// Initialize
	*service = NULL;

	try
	{
		object = new CComObject<CDNSSDService>();
	}
	catch ( ... )
	{
		object = NULL;
	}

	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
	hr = object->FinalConstruct();
	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );
	object->AddRef();

	prot = ( addressFamily | protocol );

	err = DNSServiceNATPortMappingCreate( &sref, flags, ifIndex, prot, internalPort, externalPort, ttl, ( DNSServiceNATPortMappingReply ) &NATPortMappingReply, object );
	require_noerr( err, exit );

	object->SetServiceRef( sref );
	object->SetListener( listener );

	err = object->Run();
	require_noerr( err, exit );

	*service = object;

exit:

	if ( err && object )
	{
		object->Release();
	}

	return err;
}


STDMETHODIMP CDNSSD::GetProperty(BSTR prop, VARIANT * value )
{
	std::string			propUTF8;
	std::vector< BYTE >	byteArray;
	SAFEARRAY		*	psa			= NULL;
	BYTE			*	pData		= NULL;
	uint32_t			elems		= 0;
	DNSServiceErrorType	err			= 0;
	BOOL				ok = TRUE;

	// Convert BSTR params to utf8
	ok = BSTRToUTF8( prop, propUTF8 );
	require_action( ok, exit, err = kDNSServiceErr_BadParam );

	// Setup the byte array
	require_action( V_VT( value ) == ( VT_ARRAY|VT_UI1 ), exit, err = kDNSServiceErr_Unknown );
	psa = V_ARRAY( value );
	require_action( psa, exit, err = kDNSServiceErr_Unknown );
	require_action( SafeArrayGetDim( psa ) == 1, exit, err = kDNSServiceErr_Unknown );
	byteArray.reserve( psa->rgsabound[0].cElements );
	byteArray.assign( byteArray.capacity(), 0 );
	elems = ( uint32_t ) byteArray.capacity();

	// Call the function and package the return value in the Variant
	err = DNSServiceGetProperty( propUTF8.c_str(), &byteArray[ 0 ], &elems );
	require_noerr( err, exit );
	ok = ByteArrayToVariant( &byteArray[ 0 ], elems, value );
	require_action( ok, exit, err = kDNSSDError_Unknown );

exit:

	if ( psa )
	{
		SafeArrayUnaccessData( psa );
		psa = NULL;
	}

	return err;
}


void DNSSD_API
CDNSSD::DomainEnumReply
    (
    DNSServiceRef                       sdRef,
    DNSServiceFlags                     flags,
    uint32_t                            ifIndex,
    DNSServiceErrorType                 errorCode,
    const char                          *replyDomainUTF8,
    void                                *context
    )
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IDomainListener	* listener;

		listener = ( IDomainListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR replyDomain;
		
			UTF8ToBSTR( replyDomainUTF8, replyDomain );

			if ( flags & kDNSServiceFlagsAdd )
			{
				listener->DomainFound( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );
			}
			else
			{
				listener->DomainLost( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );
			}
		}
		else
		{
			listener->EnumDomainsFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::BrowseReply
		(
		DNSServiceRef                       sdRef,
		DNSServiceFlags                     flags,
		uint32_t                            ifIndex,
		DNSServiceErrorType                 errorCode,
		const char                          *serviceNameUTF8,
		const char                          *regTypeUTF8,
		const char                          *replyDomainUTF8,
		void                                *context
		)
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IBrowseListener	* listener;

		listener = ( IBrowseListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR	serviceName;
			CComBSTR	regType;
			CComBSTR	replyDomain;
		
			UTF8ToBSTR( serviceNameUTF8, serviceName );
			UTF8ToBSTR( regTypeUTF8, regType );
			UTF8ToBSTR( replyDomainUTF8, replyDomain );

			if ( flags & kDNSServiceFlagsAdd )
			{
				listener->ServiceFound( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );
			}
			else
			{
				listener->ServiceLost( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );
			}
		}
		else
		{
			listener->BrowseFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::ResolveReply
		(
		DNSServiceRef                       sdRef,
		DNSServiceFlags                     flags,
		uint32_t                            ifIndex,
		DNSServiceErrorType                 errorCode,
		const char                          *fullNameUTF8,
		const char                          *hostNameUTF8,
		uint16_t                            port,
		uint16_t                            txtLen,
		const unsigned char                 *txtRecord,
		void                                *context
		)
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IResolveListener * listener;

		listener = ( IResolveListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR					fullName;
			CComBSTR					hostName;
			CComBSTR					regType;
			CComBSTR					replyDomain;
			CComObject< CTXTRecord >*	record;
			BOOL						ok;

			ok = UTF8ToBSTR( fullNameUTF8, fullName );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );
			ok = UTF8ToBSTR( hostNameUTF8, hostName );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );

			try
			{
				record = new CComObject<CTXTRecord>();
			}
			catch ( ... )
			{
				record = NULL;
			}

			require_action( record, exit, err = kDNSServiceErr_NoMemory );
			record->AddRef();

			char buf[ 64 ];
			sprintf( buf, "txtLen = %d", txtLen );
			OutputDebugStringA( buf );

			if ( txtLen > 0 )
			{
				record->SetBytes( txtRecord, txtLen );
			}

			listener->ServiceResolved( service, ( DNSSDFlags ) flags, ifIndex, fullName, hostName, port, record );
		}
		else
		{
			listener->ResolveFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::RegisterReply
		(
		DNSServiceRef                       sdRef,
		DNSServiceFlags                     flags,
		DNSServiceErrorType                 errorCode,
		const char                          *serviceNameUTF8,
		const char                          *regTypeUTF8,
		const char                          *domainUTF8,
		void                                *context
		)
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IRegisterListener * listener;

		listener = ( IRegisterListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR					serviceName;
			CComBSTR					regType;
			CComBSTR					domain;
			BOOL						ok;

			ok = UTF8ToBSTR( serviceNameUTF8, serviceName );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );
			ok = UTF8ToBSTR( regTypeUTF8, regType );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );
			ok = UTF8ToBSTR( domainUTF8, domain );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );

			listener->ServiceRegistered( service, ( DNSSDFlags ) flags, serviceName, regType, domain );
		}
		else
		{
			listener->ServiceRegisterFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::QueryRecordReply
		(
		DNSServiceRef                       sdRef,
		DNSServiceFlags                     flags,
		uint32_t                            ifIndex,
		DNSServiceErrorType                 errorCode,
		const char                          *fullNameUTF8,
		uint16_t                            rrtype,
		uint16_t                            rrclass,
		uint16_t                            rdlen,
		const void                          *rdata,
		uint32_t                            ttl,
		void                                *context
		)
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IQueryRecordListener * listener;

		listener = ( IQueryRecordListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR	fullName;
			VARIANT		var;
			BOOL		ok;

			ok = UTF8ToBSTR( fullNameUTF8, fullName );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );
			ok = ByteArrayToVariant( rdata, rdlen, &var );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );

			listener->QueryRecordAnswered( service, ( DNSSDFlags ) flags, ifIndex, fullName, ( DNSSDRRType ) rrtype, ( DNSSDRRClass ) rrclass, var, ttl );
		}
		else
		{
			listener->QueryRecordFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::GetAddrInfoReply
		(
		DNSServiceRef                    sdRef,
		DNSServiceFlags                  flags,
		uint32_t                         ifIndex,
		DNSServiceErrorType              errorCode,
		const char                       *hostNameUTF8,
		const struct sockaddr            *rawAddress,
		uint32_t                         ttl,
		void                             *context
		)
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		IGetAddrInfoListener * listener;

		listener = ( IGetAddrInfoListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			CComBSTR			hostName;
			DWORD				sockaddrLen;
			DNSSDAddressFamily	addressFamily;
			char				addressUTF8[INET6_ADDRSTRLEN];
			DWORD				addressLen = sizeof( addressUTF8 );
			CComBSTR			address;
			BOOL				ok;

			ok = UTF8ToBSTR( hostNameUTF8, hostName );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );

			switch ( rawAddress->sa_family )
			{
				case AF_INET:
				{
					addressFamily	= kDNSSDAddressFamily_IPv4;
					sockaddrLen		= sizeof( sockaddr_in );
				}
				break;

				case AF_INET6:
				{
					addressFamily	= kDNSSDAddressFamily_IPv6;
					sockaddrLen		= sizeof( sockaddr_in6 );
				}
				break;
			}

			err = WSAAddressToStringA( ( LPSOCKADDR ) rawAddress, sockaddrLen, NULL, addressUTF8, &addressLen );
			require_noerr( err, exit );
			ok = UTF8ToBSTR( addressUTF8, address );
			require_action( ok, exit, err = kDNSServiceErr_Unknown );

			listener->GetAddrInfoReply( service, ( DNSSDFlags ) flags, ifIndex, hostName, addressFamily, address, ttl );
		}
		else
		{
			listener->GetAddrInfoFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}


void DNSSD_API
CDNSSD::NATPortMappingReply
    (
    DNSServiceRef                    sdRef,
    DNSServiceFlags                  flags,
    uint32_t                         ifIndex,
    DNSServiceErrorType              errorCode,
    uint32_t                         externalAddress,   /* four byte IPv4 address in network byte order */
    DNSServiceProtocol               protocol,
    uint16_t                         internalPort,
    uint16_t                         externalPort,      /* may be different than the requested port     */
    uint32_t                         ttl,               /* may be different than the requested ttl      */
    void                             *context
    )
{
	CComObject<CDNSSDService> * service;
	int err;
	
	service = ( CComObject< CDNSSDService>* ) context;
	require_action( service, exit, err = kDNSServiceErr_Unknown );

	if ( !service->Stopped() )
	{
		INATPortMappingListener * listener;

		listener = ( INATPortMappingListener* ) service->GetListener();
		require_action( listener, exit, err = kDNSServiceErr_Unknown );

		if ( !errorCode )
		{
			listener->MappingCreated( service, ( DNSSDFlags ) flags, ifIndex, externalAddress, ( DNSSDAddressFamily ) ( protocol & 0x8 ), ( DNSSDProtocol ) ( protocol & 0x80 ), internalPort, externalPort, ttl  );
		}
		else
		{
			listener->MappingFailed( service, ( DNSSDError ) errorCode );
		}
	}

exit:

	return;
}