// 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;
}