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