/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // This is the main DLL file. #include "stdafx.h" #include "dnssd_NET.h" #include "DebugServices.h" #include "PString.h" using namespace System::Net::Sockets; using namespace System::Diagnostics; using namespace Apple; using namespace Apple::DNSSD; //=========================================================================================================================== // Constants //=========================================================================================================================== #define DEBUG_NAME "[dnssd.NET] " // // ConvertToString // static String* ConvertToString(const char * utf8String) { return __gc new String(utf8String, 0, strlen(utf8String), __gc new UTF8Encoding(true, true)); } // // class ServiceRef // // ServiceRef serves as the base class for all DNSService operations. // // It manages the DNSServiceRef, and implements processing the // result // ServiceRef::ServiceRef(Object * callback) : m_bDisposed(false), m_callback(callback), m_thread(NULL) { m_impl = new ServiceRefImpl(this); } ServiceRef::~ServiceRef() { } // // StartThread // // Starts the main processing thread // void ServiceRef::StartThread() { check( m_impl != NULL ); m_impl->SetupEvents(); m_thread = new Thread(new ThreadStart(this, &Apple::DNSSD::ServiceRef::ProcessingThread)); m_thread->Name = S"DNSService Thread"; m_thread->IsBackground = true; m_thread->Start(); } // // ProcessingThread // // The Thread class can only invoke methods in MC++ types. So we // make a ProcessingThread method that forwards to the impl // void ServiceRef::ProcessingThread() { m_impl->ProcessingThread(); } // // Dispose // // Calls impl-Dispose(). This ultimately will call DNSServiceRefDeallocate() // void ServiceRef::Dispose() { check(m_impl != NULL); check(m_bDisposed == false); if (!m_bDisposed) { m_bDisposed = true; // // Call Dispose. This won't call DNSServiceRefDeallocate() // necessarily. It depends on what thread this is being // called in. // m_impl->Dispose(); m_impl = NULL; m_thread = NULL; GC::SuppressFinalize(this); } } // // EnumerateDomainsDispatch // // Dispatch a reply to the delegate. // void ServiceRef::EnumerateDomainsDispatch ( ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String * replyDomain ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::EnumerateDomainsReply * OnEnumerateDomainsReply = static_cast(m_callback); OnEnumerateDomainsReply(this, flags, interfaceIndex, errorCode, replyDomain); } } // // RegisterDispatch // // Dispatch a reply to the delegate. // void ServiceRef::RegisterDispatch ( ServiceFlags flags, ErrorCode errorCode, String * name, String * regtype, String * domain ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::RegisterReply * OnRegisterReply = static_cast(m_callback); OnRegisterReply(this, flags, errorCode, name, regtype, domain); } } // // BrowseDispatch // // Dispatch a reply to the delegate. // void ServiceRef::BrowseDispatch ( ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String * serviceName, String * regtype, String * replyDomain ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::BrowseReply * OnBrowseReply = static_cast(m_callback); OnBrowseReply(this, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain); } } // // ResolveDispatch // // Dispatch a reply to the delegate. // void ServiceRef::ResolveDispatch ( ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String * fullname, String * hosttarget, int port, Byte txtRecord[] ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::ResolveReply * OnResolveReply = static_cast(m_callback); OnResolveReply(this, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord); } } // // RegisterRecordDispatch // // Dispatch a reply to the delegate. // void ServiceRef::RegisterRecordDispatch ( ServiceFlags flags, ErrorCode errorCode, RecordRef * record ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::RegisterRecordReply * OnRegisterRecordReply = static_cast(m_callback); OnRegisterRecordReply(this, flags, errorCode, record); } } // // QueryRecordDispatch // // Dispatch a reply to the delegate. // void ServiceRef::QueryRecordDispatch ( ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String * fullname, int rrtype, int rrclass, Byte rdata[], int ttl ) { if ((m_callback != NULL) && (m_impl != NULL)) { DNSService::QueryRecordReply * OnQueryRecordReply = static_cast(m_callback); OnQueryRecordReply(this, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdata, ttl); } } // // ServiceRefImpl::ServiceRefImpl() // // Constructs a new ServiceRefImpl. We save the pointer to our enclosing // class in a gcroot handle. This satisfies the garbage collector as // the outer class is a managed type // ServiceRef::ServiceRefImpl::ServiceRefImpl(ServiceRef * outer) : m_socketEvent(NULL), m_stopEvent(NULL), m_disposed(false), m_outer(outer), m_ref(NULL) { m_threadId = GetCurrentThreadId(); } // // ServiceRefImpl::~ServiceRefImpl() // // Deallocate all resources associated with the ServiceRefImpl // ServiceRef::ServiceRefImpl::~ServiceRefImpl() { if (m_socketEvent != NULL) { CloseHandle(m_socketEvent); m_socketEvent = NULL; } if (m_stopEvent != NULL) { CloseHandle(m_stopEvent); m_stopEvent = NULL; } if (m_ref != NULL) { DNSServiceRefDeallocate(m_ref); m_ref = NULL; } } // // ServiceRefImpl::SetupEvents() // // Setup the events necessary to manage multi-threaded dispatch // of DNSService Events // void ServiceRef::ServiceRefImpl::SetupEvents() { check(m_ref != NULL); m_socket = (SOCKET) DNSServiceRefSockFD(m_ref); check(m_socket != INVALID_SOCKET); m_socketEvent = CreateEvent(NULL, 0, 0, NULL); if (m_socketEvent == NULL) { throw new DNSServiceException(Unknown); } int err = WSAEventSelect(m_socket, m_socketEvent, FD_READ|FD_CLOSE); if (err != 0) { throw new DNSServiceException(Unknown); } m_stopEvent = CreateEvent(NULL, 0, 0, NULL); if (m_stopEvent == NULL) { throw new DNSServiceException(Unknown); } } // // ServiceRefImpl::ProcessingThread() // // Wait for socket events on the DNSServiceRefSockFD(). Also wait // for stop events // void ServiceRef::ServiceRefImpl::ProcessingThread() { check( m_socketEvent != NULL ); check( m_stopEvent != NULL ); check( m_ref != NULL ); HANDLE handles[2]; handles[0] = m_socketEvent; handles[1] = m_stopEvent; while (m_disposed == false) { int ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE); // // it's a socket event // if (ret == WAIT_OBJECT_0) { DNSServiceProcessResult(m_ref); } // // else it's a stop event // else if (ret == WAIT_OBJECT_0 + 1) { break; } else { // // unexpected wait result // dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, ret ); } } delete this; } // // ServiceRefImpl::Dispose() // // Calls DNSServiceRefDeallocate() // void ServiceRef::ServiceRefImpl::Dispose() { OSStatus err; BOOL ok; check(m_disposed == false); m_disposed = true; ok = SetEvent(m_stopEvent); err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); exit: return; } // // ServiceRefImpl::EnumerateDomainsCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::EnumerateDomainsCallback ( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char * replyDomain, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { self->m_outer->EnumerateDomainsDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(replyDomain)); } } // // ServiceRefImpl::RegisterCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::RegisterCallback ( DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char * name, const char * regtype, const char * domain, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { self->m_outer->RegisterDispatch((ServiceFlags) flags, (ErrorCode) errorCode, ConvertToString(name), ConvertToString(regtype), ConvertToString(domain)); } } // // ServiceRefImpl::BrowseCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::BrowseCallback ( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char * serviceName, const char * regtype, const char * replyDomain, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { self->m_outer->BrowseDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(serviceName), ConvertToString(regtype), ConvertToString(replyDomain)); } } // // ServiceRefImpl::ResolveCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::ResolveCallback ( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char * fullname, const char * hosttarget, uint16_t notAnIntPort, uint16_t txtLen, const char * txtRecord, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { Byte txtRecordBytes[]; txtRecordBytes = NULL; if (txtLen > 0) { // // copy raw memory into managed byte array // txtRecordBytes = new Byte[txtLen]; Byte __pin * p = &txtRecordBytes[0]; memcpy(p, txtRecord, txtLen); } self->m_outer->ResolveDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), ConvertToString(hosttarget), ntohs(notAnIntPort), txtRecordBytes); } } // // ServiceRefImpl::RegisterRecordCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::RegisterRecordCallback ( DNSServiceRef sdRef, DNSRecordRef rrRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { RecordRef * record = NULL; if (errorCode == 0) { record = new RecordRef; record->m_impl->m_ref = rrRef; } self->m_outer->RegisterRecordDispatch((ServiceFlags) flags, (ErrorCode) errorCode, record); } } // // ServiceRefImpl::QueryRecordCallback() // // This is the callback from dnssd.dll. We pass this up to our outer, managed type // void DNSSD_API ServiceRef::ServiceRefImpl::QueryRecordCallback ( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char * fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void * rdata, uint32_t ttl, void * context ) { ServiceRef::ServiceRefImpl * self = static_cast(context); check( self != NULL ); check( self->m_outer != NULL ); if (self->m_disposed == false) { Byte rdataBytes[]; if (rdlen) { rdataBytes = new Byte[rdlen]; Byte __pin * p = &rdataBytes[0]; memcpy(p, rdata, rdlen); } self->m_outer->QueryRecordDispatch((ServiceFlags) flags, (int) interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), rrtype, rrclass, rdataBytes, ttl); } } /* * EnumerateDomains() * * This maps to DNSServiceEnumerateDomains(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::EnumerateDomains ( int flags, int interfaceIndex, EnumerateDomainsReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); int err; err = DNSServiceEnumerateDomains(&sdRef->m_impl->m_ref, flags, interfaceIndex, ServiceRef::ServiceRefImpl::EnumerateDomainsCallback, sdRef->m_impl); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * Register() * * This maps to DNSServiceRegister(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::Register ( int flags, int interfaceIndex, String * name, String * regtype, String * domain, String * host, int port, Byte txtRecord[], RegisterReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); PString * pName = new PString(name); PString * pType = new PString(regtype); PString * pDomain = new PString(domain); PString * pHost = new PString(host); int len = 0; Byte __pin * p = NULL; void * v = NULL; if ((txtRecord != NULL) && (txtRecord->Length > 0)) { len = txtRecord->Length; p = &txtRecord[0]; v = (void*) p; } int err = DNSServiceRegister(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(), pHost->c_str(), htons(port), len, v, ServiceRef::ServiceRefImpl::RegisterCallback, sdRef->m_impl ); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * AddRecord() * * This maps to DNSServiceAddRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ RecordRef* DNSService::AddRecord ( ServiceRef * sdRef, int flags, int rrtype, Byte rdata[], int ttl ) { int len = 0; Byte __pin * p = NULL; void * v = NULL; if ((rdata != NULL) && (rdata->Length > 0)) { len = rdata->Length; p = &rdata[0]; v = (void*) p; } RecordRef * record = new RecordRef; int err = DNSServiceAddRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, rrtype, len, v, ttl); if (err != 0) { throw new DNSServiceException(err); } return record; } /* * UpdateRecord() * * This maps to DNSServiceUpdateRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ void DNSService::UpdateRecord ( ServiceRef * sdRef, RecordRef * record, int flags, Byte rdata[], int ttl ) { int len = 0; Byte __pin * p = NULL; void * v = NULL; if ((rdata != NULL) && (rdata->Length > 0)) { len = rdata->Length; p = &rdata[0]; v = (void*) p; } int err = DNSServiceUpdateRecord(sdRef->m_impl->m_ref, record ? record->m_impl->m_ref : NULL, flags, len, v, ttl); if (err != 0) { throw new DNSServiceException(err); } } /* * RemoveRecord() * * This maps to DNSServiceRemoveRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ void DNSService::RemoveRecord ( ServiceRef * sdRef, RecordRef * record, int flags ) { int err = DNSServiceRemoveRecord(sdRef->m_impl->m_ref, record->m_impl->m_ref, flags); if (err != 0) { throw new DNSServiceException(err); } } /* * Browse() * * This maps to DNSServiceBrowse(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::Browse ( int flags, int interfaceIndex, String * regtype, String * domain, BrowseReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); PString * pType = new PString(regtype); PString * pDomain = new PString(domain); int err = DNSServiceBrowse(&sdRef->m_impl->m_ref, flags, interfaceIndex, pType->c_str(), pDomain->c_str(),(DNSServiceBrowseReply) ServiceRef::ServiceRefImpl::BrowseCallback, sdRef->m_impl); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * Resolve() * * This maps to DNSServiceResolve(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::Resolve ( int flags, int interfaceIndex, String * name, String * regtype, String * domain, ResolveReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); PString * pName = new PString(name); PString * pType = new PString(regtype); PString * pDomain = new PString(domain); int err = DNSServiceResolve(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(),(DNSServiceResolveReply) ServiceRef::ServiceRefImpl::ResolveCallback, sdRef->m_impl); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * CreateConnection() * * This maps to DNSServiceCreateConnection(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::CreateConnection ( RegisterRecordReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); int err = DNSServiceCreateConnection(&sdRef->m_impl->m_ref); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * RegisterRecord() * * This maps to DNSServiceRegisterRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ RecordRef* DNSService::RegisterRecord ( ServiceRef * sdRef, ServiceFlags flags, int interfaceIndex, String * fullname, int rrtype, int rrclass, Byte rdata[], int ttl ) { RecordRef * record = new RecordRef; int len = 0; Byte __pin * p = NULL; void * v = NULL; PString * pFullname = new PString(fullname); if ((rdata != NULL) && (rdata->Length > 0)) { len = rdata->Length; p = &rdata[0]; v = (void*) p; } int err = DNSServiceRegisterRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v, ttl, (DNSServiceRegisterRecordReply) ServiceRef::ServiceRefImpl::RegisterRecordCallback, sdRef->m_impl); if (err != 0) { throw new DNSServiceException(err); } return record; } /* * QueryRecord() * * This maps to DNSServiceQueryRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ ServiceRef* DNSService::QueryRecord ( ServiceFlags flags, int interfaceIndex, String * fullname, int rrtype, int rrclass, QueryRecordReply * callback ) { ServiceRef * sdRef = new ServiceRef(callback); PString * pFullname = new PString(fullname); int err = DNSServiceQueryRecord(&sdRef->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, (DNSServiceQueryRecordReply) ServiceRef::ServiceRefImpl::QueryRecordCallback, sdRef->m_impl); if (err != 0) { throw new DNSServiceException(err); } sdRef->StartThread(); return sdRef; } /* * ReconfirmRecord() * * This maps to DNSServiceReconfirmRecord(). Returns an * initialized ServiceRef on success, throws an exception * on failure. */ void DNSService::ReconfirmRecord ( ServiceFlags flags, int interfaceIndex, String * fullname, int rrtype, int rrclass, Byte rdata[] ) { int len = 0; Byte __pin * p = NULL; void * v = NULL; PString * pFullname = new PString(fullname); if ((rdata != NULL) && (rdata->Length > 0)) { len = rdata->Length; p = &rdata[0]; v = (void*) p; } DNSServiceReconfirmRecord(flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v); } void TextRecord::SetValue ( String * key, Byte value[] /* may be NULL */ ) { PString * pKey = new PString(key); int len = 0; Byte __pin * p = NULL; void * v = NULL; DNSServiceErrorType err; if (value && (value->Length > 0)) { len = value->Length; p = &value[0]; v = (void*) p; } err = TXTRecordSetValue(&m_impl->m_ref, pKey->c_str(), len, v); if (err != 0) { throw new DNSServiceException(err); } } void TextRecord::RemoveValue ( String * key ) { PString * pKey = new PString(key); DNSServiceErrorType err; err = TXTRecordRemoveValue(&m_impl->m_ref, pKey->c_str()); if (err != 0) { throw new DNSServiceException(err); } } int TextRecord::GetLength ( ) { return TXTRecordGetLength(&m_impl->m_ref); } Byte TextRecord::GetBytes ( ) __gc[] { const void * noGCBytes = NULL; Byte gcBytes[] = NULL; noGCBytes = TXTRecordGetBytesPtr(&m_impl->m_ref); int len = GetLength(); if (noGCBytes && len) { gcBytes = new Byte[len]; Byte __pin * p = &gcBytes[0]; memcpy(p, noGCBytes, len); } return gcBytes; } bool TextRecord::ContainsKey ( Byte txtRecord[], String * key ) { PString * pKey = new PString(key); Byte __pin * p = &txtRecord[0]; return (TXTRecordContainsKey(txtRecord->Length, p, pKey->c_str()) > 0) ? true : false; } Byte TextRecord::GetValueBytes ( Byte txtRecord[], String * key ) __gc[] { uint8_t valueLen; Byte ret[] = NULL; PString * pKey = new PString(key); Byte __pin * p1 = &txtRecord[0]; const void * v; v = TXTRecordGetValuePtr(txtRecord->Length, p1, pKey->c_str(), &valueLen); if (v != NULL) { ret = new Byte[valueLen]; Byte __pin * p2 = &ret[0]; memcpy(p2, v, valueLen); } return ret; } int TextRecord::GetCount ( Byte txtRecord[] ) { Byte __pin * p = &txtRecord[0]; return TXTRecordGetCount(txtRecord->Length, p); } Byte TextRecord::GetItemAtIndex ( Byte txtRecord[], int index, [Out] String ** key ) __gc[] { char keyBuf[255]; uint8_t keyBufLen = 255; uint8_t valueLen; void * value; Byte ret[] = NULL; DNSServiceErrorType err; Byte __pin * p1 = &txtRecord[0]; err = TXTRecordGetItemAtIndex(txtRecord->Length, p1, index, keyBufLen, keyBuf, &valueLen, (const void**) &value); if (err != 0) { throw new DNSServiceException(err); } *key = ConvertToString(keyBuf); if (valueLen) { ret = new Byte[valueLen]; Byte __pin * p2 = &ret[0]; memcpy(p2, value, valueLen); } return ret; } // // DNSServiceException::DNSServiceException() // // Constructs an exception with an error code // DNSServiceException::DNSServiceException ( int _err ) : err(_err) { } // // This version of the constructor is useful for instances in which // an inner exception is thrown, caught, and then a new exception // is thrown in it's place // DNSServiceException::DNSServiceException ( String * message, System::Exception * innerException ) { }