diff options
Diffstat (limited to 'netwerk/dns')
-rw-r--r-- | netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp | 779 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/MDNSResponderOperator.h | 152 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp | 302 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/MDNSResponderReply.h | 164 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/moz.build | 38 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp | 262 | ||||
-rw-r--r-- | netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h | 49 |
7 files changed, 1733 insertions, 13 deletions
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp new file mode 100644 index 0000000000..72b5577749 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp @@ -0,0 +1,779 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MDNSResponderOperator.h" +#include "MDNSResponderReply.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Unused.h" +#include "nsComponentManagerUtils.h" +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsDNSServiceInfo.h" +#include "nsHashPropertyBag.h" +#include "nsIProperty.h" +#include "nsISimpleEnumerator.h" +#include "nsIVariant.h" +#include "nsServiceManagerUtils.h" +#include "nsNetAddr.h" +#include "nsNetCID.h" +#include "nsSocketTransportService2.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCID.h" +#include "private/pprio.h" + +#include "nsASocketHandler.h" + +namespace mozilla { +namespace net { + +static LazyLogModule gMDNSLog("MDNSResponderOperator"); +#undef LOG_I +#define LOG_I(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +#undef LOG_E +#define LOG_E(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__)) + +class MDNSResponderOperator::ServiceWatcher final + : public nsASocketHandler +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + // nsASocketHandler methods + virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override + { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(fd == mFD); + + if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) { + LOG_E("error polling on listening socket (%p)", fd); + mCondition = NS_ERROR_UNEXPECTED; + } + + if (!(outFlags & PR_POLL_READ)) { + return; + } + + DNSServiceProcessResult(mService); + } + + virtual void OnSocketDetached(PRFileDesc *fd) override + { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(mThread); + MOZ_ASSERT(fd == mFD); + + if (!mFD) { + return; + } + + // Bug 1175387: do not double close the handle here. + PR_ChangeFileDescNativeHandle(mFD, -1); + PR_Close(mFD); + mFD = nullptr; + + mThread->Dispatch(NewRunnableMethod(this, &ServiceWatcher::Deallocate), + NS_DISPATCH_NORMAL); + } + + virtual void IsLocal(bool *aIsLocal) override { *aIsLocal = true; } + + virtual void KeepWhenOffline(bool *aKeepWhenOffline) override + { + *aKeepWhenOffline = true; + } + + virtual uint64_t ByteCountSent() override { return 0; } + virtual uint64_t ByteCountReceived() override { return 0; } + + explicit ServiceWatcher(DNSServiceRef aService, + MDNSResponderOperator* aOperator) + : mThread(nullptr) + , mSts(nullptr) + , mOperatorHolder(aOperator) + , mService(aService) + , mFD(nullptr) + , mAttached(false) + { + if (!gSocketTransportService) + { + nsCOMPtr<nsISocketTransportService> sts = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); + } + } + + nsresult Init() + { + MOZ_ASSERT(PR_GetCurrentThread() != gSocketThread); + mThread = NS_GetCurrentThread(); + + if (!mService) { + return NS_OK; + } + + if (!gSocketTransportService) { + return NS_ERROR_FAILURE; + } + mSts = gSocketTransportService; + + int osfd = DNSServiceRefSockFD(mService); + if (osfd == -1) { + return NS_ERROR_FAILURE; + } + + mFD = PR_ImportFile(osfd); + return PostEvent(&ServiceWatcher::OnMsgAttach); + } + + void Close() + { + MOZ_ASSERT(PR_GetCurrentThread() != gSocketThread); + + if (!gSocketTransportService) { + Deallocate(); + return; + } + + PostEvent(&ServiceWatcher::OnMsgClose); + } + +private: + ~ServiceWatcher() = default; + + void Deallocate() + { + if (mService) { + DNSServiceRefDeallocate(mService); + mService = nullptr; + } + mOperatorHolder = nullptr; + } + + nsresult PostEvent(void(ServiceWatcher::*func)(void)) + { + return gSocketTransportService->Dispatch(NewRunnableMethod(this, func), + NS_DISPATCH_NORMAL); + } + + void OnMsgClose() + { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + if (NS_FAILED(mCondition)) { + return; + } + + // tear down socket. this signals the STS to detach our socket handler. + mCondition = NS_BINDING_ABORTED; + + // if we are attached, then socket transport service will call our + // OnSocketDetached method automatically. Otherwise, we have to call it + // (and thus close the socket) manually. + if (!mAttached) { + OnSocketDetached(mFD); + } + } + + void OnMsgAttach() + { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + if (NS_FAILED(mCondition)) { + return; + } + + mCondition = TryAttach(); + + // if we hit an error while trying to attach then bail... + if (NS_FAILED(mCondition)) { + NS_ASSERTION(!mAttached, "should not be attached already"); + OnSocketDetached(mFD); + } + + } + + nsresult TryAttach() + { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + nsresult rv; + + if (!gSocketTransportService) { + return NS_ERROR_FAILURE; + } + + // + // find out if it is going to be ok to attach another socket to the STS. + // if not then we have to wait for the STS to tell us that it is ok. + // the notification is asynchronous, which means that when we could be + // in a race to call AttachSocket once notified. for this reason, when + // we get notified, we just re-enter this function. as a result, we are + // sure to ask again before calling AttachSocket. in this way we deal + // with the race condition. though it isn't the most elegant solution, + // it is far simpler than trying to build a system that would guarantee + // FIFO ordering (which wouldn't even be that valuable IMO). see bug + // 194402 for more info. + // + if (!gSocketTransportService->CanAttachSocket()) { + nsCOMPtr<nsIRunnable> event = + NewRunnableMethod(this, &ServiceWatcher::OnMsgAttach); + + nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); + if (NS_FAILED(rv)) { + return rv; + } + } + + // + // ok, we can now attach our socket to the STS for polling + // + rv = gSocketTransportService->AttachSocket(mFD, this); + if (NS_FAILED(rv)) { + return rv; + } + + mAttached = true; + + // + // now, configure our poll flags for listening... + // + mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); + + return NS_OK; + } + + nsCOMPtr<nsIThread> mThread; + RefPtr<nsSocketTransportService> mSts; + RefPtr<MDNSResponderOperator> mOperatorHolder; + DNSServiceRef mService; + PRFileDesc* mFD; + bool mAttached; +}; + +NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher, nsISupports) + +MDNSResponderOperator::MDNSResponderOperator() + : mService(nullptr) + , mWatcher(nullptr) + , mThread(NS_GetCurrentThread()) + , mIsCancelled(false) +{ +} + +MDNSResponderOperator::~MDNSResponderOperator() +{ + Stop(); +} + +nsresult +MDNSResponderOperator::Start() +{ + if (mIsCancelled) { + return NS_OK; + } + + if (IsServing()) { + Stop(); + } + + return NS_OK; +} + +nsresult +MDNSResponderOperator::Stop() +{ + return ResetService(nullptr); +} + +nsresult +MDNSResponderOperator::ResetService(DNSServiceRef aService) +{ + nsresult rv; + + if (aService != mService) { + if (mWatcher) { + mWatcher->Close(); + mWatcher = nullptr; + } + + if (aService) { + RefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService, this); + if (NS_WARN_IF(NS_FAILED(rv = watcher->Init()))) { + return rv; + } + mWatcher = watcher; + } + + mService = aService; + } + return NS_OK; +} + +BrowseOperator::BrowseOperator(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener) + : MDNSResponderOperator() + , mServiceType(aServiceType) + , mListener(aListener) +{ +} + +nsresult +BrowseOperator::Start() +{ + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = DNSServiceBrowse(&service, + 0, + kDNSServiceInterfaceIndexAny, + mServiceType.get(), + nullptr, + &BrowseReplyRunnable::Reply, + this); + NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, "DNSServiceBrowse fail"); + + if (mListener) { + if (kDNSServiceErr_NoError == err) { + mListener->OnDiscoveryStarted(mServiceType); + } else { + mListener->OnStartDiscoveryFailed(mServiceType, err); + } + } + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +nsresult +BrowseOperator::Stop() +{ + bool isServing = IsServing(); + nsresult rv = MDNSResponderOperator::Stop(); + + if (isServing && mListener) { + if (NS_SUCCEEDED(rv)) { + mListener->OnDiscoveryStopped(mServiceType); + } else { + mListener->OnStopDiscoveryFailed(mServiceType, + static_cast<uint32_t>(rv)); + } + } + + return rv; +} + +void +BrowseOperator::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain) +{ + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("BrowseOperator::Reply (%d)", aErrorCode); + if (mListener) { + mListener->OnStartDiscoveryFailed(mServiceType, aErrorCode); + } + return; + } + + if (!mListener) { return; } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(); + + if (NS_WARN_IF(!info)) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aServiceName)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aReplyDomain)))) { return; } + + if (aFlags & kDNSServiceFlagsAdd) { + mListener->OnServiceFound(info); + } else { + mListener->OnServiceLost(info); + } +} + +RegisterOperator::RegisterOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener) + : MDNSResponderOperator() + , mServiceInfo(aServiceInfo) + , mListener(aListener) +{ +} + +nsresult +RegisterOperator::Start() +{ + nsresult rv; + + rv = MDNSResponderOperator::Start(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + uint16_t port; + if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetPort(&port)))) { + return rv; + } + nsAutoCString type; + if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetServiceType(type)))) { + return rv; + } + + TXTRecordRef txtRecord; + char buf[TXT_BUFFER_SIZE] = { 0 }; + TXTRecordCreate(&txtRecord, TXT_BUFFER_SIZE, buf); + + nsCOMPtr<nsIPropertyBag2> attributes; + if (NS_FAILED(rv = mServiceInfo->GetAttributes(getter_AddRefs(attributes)))) { + LOG_I("register: no attributes"); + } else { + nsCOMPtr<nsISimpleEnumerator> enumerator; + if (NS_WARN_IF(NS_FAILED(rv = + attributes->GetEnumerator(getter_AddRefs(enumerator))))) { + return rv; + } + + bool hasMoreElements; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && + hasMoreElements) { + nsCOMPtr<nsISupports> element; + MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element))); + nsCOMPtr<nsIProperty> property = do_QueryInterface(element); + MOZ_ASSERT(property); + + nsAutoString name; + nsCOMPtr<nsIVariant> value; + MOZ_ALWAYS_SUCCEEDS(property->GetName(name)); + MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value))); + + nsAutoCString str; + if (NS_WARN_IF(NS_FAILED(value->GetAsACString(str)))) { + continue; + } + + TXTRecordSetValue(&txtRecord, + /* it's safe because key name is ASCII only. */ + NS_LossyConvertUTF16toASCII(name).get(), + str.Length(), + str.get()); + } + } + + nsAutoCString host; + nsAutoCString name; + nsAutoCString domain; + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = + DNSServiceRegister(&service, + 0, + 0, + NS_SUCCEEDED(mServiceInfo->GetServiceName(name)) ? + name.get() : nullptr, + type.get(), + NS_SUCCEEDED(mServiceInfo->GetDomainName(domain)) ? + domain.get() : nullptr, + NS_SUCCEEDED(mServiceInfo->GetHost(host)) ? + host.get() : nullptr, + NativeEndian::swapToNetworkOrder(port), + TXTRecordGetLength(&txtRecord), + TXTRecordGetBytesPtr(&txtRecord), + &RegisterReplyRunnable::Reply, + this); + NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, + "DNSServiceRegister fail"); + + TXTRecordDeallocate(&txtRecord); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnRegistrationFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +nsresult +RegisterOperator::Stop() +{ + bool isServing = IsServing(); + nsresult rv = MDNSResponderOperator::Stop(); + + if (isServing && mListener) { + if (NS_SUCCEEDED(rv)) { + mListener->OnServiceUnregistered(mServiceInfo); + } else { + mListener->OnUnregistrationFailed(mServiceInfo, + static_cast<uint32_t>(rv)); + } + } + + return rv; +} + +void +RegisterOperator::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& aDomain) +{ + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + if (kDNSServiceErr_NoError != aErrorCode) { + LOG_E("RegisterOperator::Reply (%d)", aErrorCode); + } + + if (!mListener) { return; } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aName)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aDomain)))) { return; } + + if (kDNSServiceErr_NoError == aErrorCode) { + if (aFlags & kDNSServiceFlagsAdd) { + mListener->OnServiceRegistered(info); + } else { + // If a successfully-registered name later suffers a name conflict + // or similar problem and has to be deregistered, the callback will + // be invoked with the kDNSServiceFlagsAdd flag not set. + LOG_E("RegisterOperator::Reply: deregister"); + if (NS_WARN_IF(NS_FAILED(Stop()))) { + return; + } + } + } else { + mListener->OnRegistrationFailed(info, aErrorCode); + } +} + +ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener) + : MDNSResponderOperator() + , mServiceInfo(aServiceInfo) + , mListener(aListener) +{ +} + +nsresult +ResolveOperator::Start() +{ + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + nsAutoCString name; + mServiceInfo->GetServiceName(name); + nsAutoCString type; + mServiceInfo->GetServiceType(type); + nsAutoCString domain; + mServiceInfo->GetDomainName(domain); + + LOG_I("Resolve: (%s), (%s), (%s)", name.get(), type.get(), domain.get()); + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = + DNSServiceResolve(&service, + 0, + kDNSServiceInterfaceIndexAny, + name.get(), + type.get(), + domain.get(), + (DNSServiceResolveReply)&ResolveReplyRunnable::Reply, + this); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnResolveFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +void +ResolveOperator::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord) +{ + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + auto guard = MakeScopeExit([&] { + Unused << NS_WARN_IF(NS_FAILED(Stop())); + }); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("ResolveOperator::Reply (%d)", aErrorCode); + return; + } + + // Resolve TXT record + int count = TXTRecordGetCount(aTxtLen, aTxtRecord); + LOG_I("resolve: txt count = %d, len = %d", count, aTxtLen); + nsCOMPtr<nsIWritablePropertyBag2> attributes = new nsHashPropertyBag(); + if (NS_WARN_IF(!attributes)) { + return; + } + if (count) { + for (int i = 0; i < count; ++i) { + char key[TXT_BUFFER_SIZE] = { '\0' }; + uint8_t vSize = 0; + const void* value = nullptr; + if (kDNSServiceErr_NoError != + TXTRecordGetItemAtIndex(aTxtLen, + aTxtRecord, + i, + TXT_BUFFER_SIZE, + key, + &vSize, + &value)) { + break; + } + + nsAutoCString str(reinterpret_cast<const char*>(value), vSize); + LOG_I("resolve TXT: (%d) %s=%s", vSize, key, str.get()); + + if (NS_WARN_IF(NS_FAILED(attributes->SetPropertyAsACString( + /* it's safe to convert because key name is ASCII only. */ + NS_ConvertASCIItoUTF16(key), + str)))) { + break; + } + } + } + + if (!mListener) { return; } + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetHost(aHostTarget)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetPort(aPort)))) { return; } + if (NS_WARN_IF(NS_FAILED(info->SetAttributes(attributes)))) { return; } + + if (kDNSServiceErr_NoError == aErrorCode) { + GetAddrInfor(info); + } + else { + mListener->OnResolveFailed(info, aErrorCode); + Unused << NS_WARN_IF(NS_FAILED(Stop())); + } +} + +void +ResolveOperator::GetAddrInfor(nsIDNSServiceInfo* aServiceInfo) +{ + RefPtr<GetAddrInfoOperator> getAddreOp = new GetAddrInfoOperator(aServiceInfo, + mListener); + Unused << NS_WARN_IF(NS_FAILED(getAddreOp->Start())); +} + +GetAddrInfoOperator::GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener) + : MDNSResponderOperator() + , mServiceInfo(aServiceInfo) + , mListener(aListener) +{ +} + +nsresult +GetAddrInfoOperator::Start() +{ + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) { + return rv; + } + + nsAutoCString host; + mServiceInfo->GetHost(host); + + LOG_I("GetAddrInfo: (%s)", host.get()); + + DNSServiceRef service = nullptr; + DNSServiceErrorType err = + DNSServiceGetAddrInfo(&service, + kDNSServiceFlagsForceMulticast, + kDNSServiceInterfaceIndexAny, + kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, + host.get(), + (DNSServiceGetAddrInfoReply)&GetAddrInfoReplyRunnable::Reply, + this); + + if (NS_WARN_IF(kDNSServiceErr_NoError != err)) { + if (mListener) { + mListener->OnResolveFailed(mServiceInfo, err); + } + return NS_ERROR_FAILURE; + } + + return ResetService(service); +} + +void +GetAddrInfoOperator::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const NetAddr& aAddress, + uint32_t aTTL) +{ + MOZ_ASSERT(GetThread() == NS_GetCurrentThread()); + + auto guard = MakeScopeExit([&] { + Unused << NS_WARN_IF(NS_FAILED(Stop())); + }); + + if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) { + LOG_E("GetAddrInfoOperator::Reply (%d)", aErrorCode); + return; + } + + if (!mListener) { return; } + + NetAddr addr = aAddress; + nsCOMPtr<nsINetAddr> address = new nsNetAddr(&addr); + nsCString addressStr; + if (NS_WARN_IF(NS_FAILED(address->GetAddress(addressStr)))) { return; } + + nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo); + if (NS_WARN_IF(NS_FAILED(info->SetAddress(addressStr)))) { return; } + + /** + * |kDNSServiceFlagsMoreComing| means this callback will be one or more + * callback events later, so this instance should be kept alive until all + * follow-up events are processed. + */ + if (aFlags & kDNSServiceFlagsMoreComing) { + guard.release(); + } + + if (kDNSServiceErr_NoError == aErrorCode) { + mListener->OnServiceResolved(info); + } else { + mListener->OnResolveFailed(info, aErrorCode); + } +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h new file mode 100644 index 0000000000..a932baa7cc --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h +#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h + +#include "dns_sd.h" +#include "mozilla/Atomics.h" +#include "mozilla/RefPtr.h" +#include "nsCOMPtr.h" +#include "nsIDNSServiceDiscovery.h" +#include "nsIThread.h" +#include "nsString.h" + +namespace mozilla { +namespace net { + +class MDNSResponderOperator +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MDNSResponderOperator) + +public: + MDNSResponderOperator(); + + virtual nsresult Start(); + virtual nsresult Stop(); + void Cancel() { mIsCancelled = true; } + nsIThread* GetThread() const { return mThread; } + +protected: + virtual ~MDNSResponderOperator(); + + bool IsServing() const { return mService; } + nsresult ResetService(DNSServiceRef aService); + +private: + class ServiceWatcher; + + DNSServiceRef mService; + RefPtr<ServiceWatcher> mWatcher; + nsCOMPtr<nsIThread> mThread; // remember caller thread for callback + Atomic<bool> mIsCancelled; +}; + +class BrowseOperator final : public MDNSResponderOperator +{ +public: + BrowseOperator(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener); + + nsresult Start() override; + nsresult Stop() override; + + void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain); + +private: + ~BrowseOperator() = default; + + nsCString mServiceType; + nsCOMPtr<nsIDNSServiceDiscoveryListener> mListener; +}; + +class RegisterOperator final : public MDNSResponderOperator +{ + enum { TXT_BUFFER_SIZE = 256 }; + +public: + RegisterOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener); + + nsresult Start() override; + nsresult Stop() override; + + void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& aDomain); + +private: + ~RegisterOperator() = default; + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSRegistrationListener> mListener; +}; + +class ResolveOperator final : public MDNSResponderOperator +{ + enum { TXT_BUFFER_SIZE = 256 }; + +public: + ResolveOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener); + + nsresult Start() override; + + void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord); + +private: + ~ResolveOperator() = default; + void GetAddrInfor(nsIDNSServiceInfo* aServiceInfo); + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSServiceResolveListener> mListener; +}; + +union NetAddr; + +class GetAddrInfoOperator final : public MDNSResponderOperator +{ +public: + GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener); + + nsresult Start() override; + + void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const NetAddr& aAddress, + uint32_t aTTL); + +private: + ~GetAddrInfoOperator() = default; + + nsCOMPtr<nsIDNSServiceInfo> mServiceInfo; + nsCOMPtr<nsIDNSServiceResolveListener> mListener; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp new file mode 100644 index 0000000000..7aa5b3759a --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MDNSResponderReply.h" +#include "mozilla/EndianUtils.h" +#include "private/pprio.h" + +namespace mozilla { +namespace net { + +BrowseReplyRunnable::BrowseReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain, + BrowseOperator* aContext) + : mSdRef(aSdRef) + , mFlags(aFlags) + , mInterfaceIndex(aInterfaceIndex) + , mErrorCode(aErrorCode) + , mServiceName(aServiceName) + , mRegType(aRegType) + , mReplyDomain(aReplyDomain) + , mContext(aContext) +{ +} + +NS_IMETHODIMP +BrowseReplyRunnable::Run() +{ + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, + mFlags, + mInterfaceIndex, + mErrorCode, + mServiceName, + mRegType, + mReplyDomain); + return NS_OK; +} + +void +BrowseReplyRunnable::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aServiceName, + const char* aRegType, + const char* aReplyDomain, + void* aContext) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + BrowseOperator* obj(reinterpret_cast<BrowseOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch(new BrowseReplyRunnable(aSdRef, + aFlags, + aInterfaceIndex, + aErrorCode, + nsCString(aServiceName), + nsCString(aRegType), + nsCString(aReplyDomain), + obj), + NS_DISPATCH_NORMAL); +} + +RegisterReplyRunnable::RegisterReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& domain, + RegisterOperator* aContext) + : mSdRef(aSdRef) + , mFlags(aFlags) + , mErrorCode(aErrorCode) + , mName(aName) + , mRegType(aRegType) + , mDomain(domain) + , mContext(aContext) +{ +} + +NS_IMETHODIMP +RegisterReplyRunnable::Run() +{ + MOZ_ASSERT(mContext); + + mContext->Reply(mSdRef, + mFlags, + mErrorCode, + mName, + mRegType, + mDomain); + return NS_OK; +} + +void +RegisterReplyRunnable::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const char* aName, + const char* aRegType, + const char* domain, + void* aContext) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + RegisterOperator* obj(reinterpret_cast<RegisterOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch(new RegisterReplyRunnable(aSdRef, + aFlags, + aErrorCode, + nsCString(aName), + nsCString(aRegType), + nsCString(domain), + obj), + NS_DISPATCH_NORMAL); +} + +ResolveReplyRunnable::ResolveReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord, + ResolveOperator* aContext) + : mSdRef(aSdRef) + , mFlags(aFlags) + , mInterfaceIndex(aInterfaceIndex) + , mErrorCode(aErrorCode) + , mFullname(aFullName) + , mHosttarget(aHostTarget) + , mPort(aPort) + , mTxtLen(aTxtLen) + , mTxtRecord(new unsigned char[aTxtLen]) + , mContext(aContext) +{ + if (mTxtRecord) { + memcpy(mTxtRecord.get(), aTxtRecord, aTxtLen); + } +} + +ResolveReplyRunnable::~ResolveReplyRunnable() +{ +} + +NS_IMETHODIMP +ResolveReplyRunnable::Run() +{ + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, + mFlags, + mInterfaceIndex, + mErrorCode, + mFullname, + mHosttarget, + mPort, + mTxtLen, + mTxtRecord.get()); + return NS_OK; +} + +void +ResolveReplyRunnable::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aFullName, + const char* aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord, + void* aContext) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + ResolveOperator* obj(reinterpret_cast<ResolveOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + thread->Dispatch(new ResolveReplyRunnable(aSdRef, + aFlags, + aInterfaceIndex, + aErrorCode, + nsCString(aFullName), + nsCString(aHostTarget), + NativeEndian::swapFromNetworkOrder(aPort), + aTxtLen, + aTxtRecord, + obj), + NS_DISPATCH_NORMAL); +} + +GetAddrInfoReplyRunnable::GetAddrInfoReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const mozilla::net::NetAddr& aAddress, + uint32_t aTTL, + GetAddrInfoOperator* aContext) + : mSdRef(aSdRef) + , mFlags(aFlags) + , mInterfaceIndex(aInterfaceIndex) + , mErrorCode(aErrorCode) + , mHostName(aHostName) + , mAddress(aAddress) + , mTTL(aTTL) + , mContext(aContext) +{ +} + +GetAddrInfoReplyRunnable::~GetAddrInfoReplyRunnable() +{ +} + +NS_IMETHODIMP +GetAddrInfoReplyRunnable::Run() +{ + MOZ_ASSERT(mContext); + mContext->Reply(mSdRef, + mFlags, + mInterfaceIndex, + mErrorCode, + mHostName, + mAddress, + mTTL); + return NS_OK; +} + +void +GetAddrInfoReplyRunnable::Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aHostName, + const struct sockaddr* aAddress, + uint32_t aTTL, + void* aContext) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + GetAddrInfoOperator* obj(reinterpret_cast<GetAddrInfoOperator*>(aContext)); + if (!obj) { + return; + } + + nsCOMPtr<nsIThread> thread(obj->GetThread()); + if (!thread) { + return; + } + + NetAddr address; + address.raw.family = aAddress->sa_family; + + static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data), + "size of sockaddr.sa_data is too big"); + memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data)); + + thread->Dispatch(new GetAddrInfoReplyRunnable(aSdRef, + aFlags, + aInterfaceIndex, + aErrorCode, + nsCString(aHostName), + address, + aTTL, + obj), + NS_DISPATCH_NORMAL); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.h b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h new file mode 100644 index 0000000000..794a585f80 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h +#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h + +#include "dns_sd.h" +#include "MDNSResponderOperator.h" +#include "mozilla/UniquePtr.h" +#include "nsIThread.h" +#include "mozilla/net/DNS.h" +#include "mozilla/RefPtr.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace net { + +class BrowseReplyRunnable final : public Runnable +{ +public: + BrowseReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aServiceName, + const nsACString& aRegType, + const nsACString& aReplyDomain, + BrowseOperator* aContext); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aServiceName, + const char* aRegType, + const char* aReplyDomain, + void* aContext); + +private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mServiceName; + nsCString mRegType; + nsCString mReplyDomain; + RefPtr<BrowseOperator> mContext; +}; + +class RegisterReplyRunnable final : public Runnable +{ +public: + RegisterReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const nsACString& aName, + const nsACString& aRegType, + const nsACString& aDomain, + RegisterOperator* aContext); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + const char* aName, + const char* aRegType, + const char* aDomain, + void* aContext); + +private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + DNSServiceErrorType mErrorCode; + nsCString mName; + nsCString mRegType; + nsCString mDomain; + RefPtr<RegisterOperator> mContext; +}; + +class ResolveReplyRunnable final : public Runnable +{ +public: + ResolveReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aFullName, + const nsACString& aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord, + ResolveOperator* aContext); + ~ResolveReplyRunnable(); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aFullName, + const char* aHostTarget, + uint16_t aPort, + uint16_t aTxtLen, + const unsigned char* aTxtRecord, + void* aContext); + +private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mFullname; + nsCString mHosttarget; + uint16_t mPort; + uint16_t mTxtLen; + UniquePtr<unsigned char> mTxtRecord; + RefPtr<ResolveOperator> mContext; +}; + +class GetAddrInfoReplyRunnable final : public Runnable +{ +public: + GetAddrInfoReplyRunnable(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const nsACString& aHostName, + const mozilla::net::NetAddr& aAddress, + uint32_t aTTL, + GetAddrInfoOperator* aContext); + ~GetAddrInfoReplyRunnable(); + + NS_IMETHOD Run() override; + + static void Reply(DNSServiceRef aSdRef, + DNSServiceFlags aFlags, + uint32_t aInterfaceIndex, + DNSServiceErrorType aErrorCode, + const char* aHostName, + const struct sockaddr* aAddress, + uint32_t aTTL, + void* aContext); + +private: + DNSServiceRef mSdRef; + DNSServiceFlags mFlags; + uint32_t mInterfaceIndex; + DNSServiceErrorType mErrorCode; + nsCString mHostName; + mozilla::net::NetAddr mAddress; + uint32_t mTTL; + RefPtr<GetAddrInfoOperator> mContext; +}; + +} // namespace net +} // namespace mozilla + + #endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h diff --git a/netwerk/dns/mdns/libmdns/moz.build b/netwerk/dns/mdns/libmdns/moz.build index 5a67c06118..23445756c6 100644 --- a/netwerk/dns/mdns/libmdns/moz.build +++ b/netwerk/dns/mdns/libmdns/moz.build @@ -3,20 +3,32 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -EXTRA_COMPONENTS += [ - 'nsDNSServiceDiscovery.js', - 'nsDNSServiceDiscovery.manifest', -] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += [ + 'MDNSResponderOperator.cpp', + 'MDNSResponderReply.cpp', + 'nsDNSServiceDiscovery.cpp', + ] -EXTRA_JS_MODULES += [ - 'fallback/DataReader.jsm', - 'fallback/DataWriter.jsm', - 'fallback/DNSPacket.jsm', - 'fallback/DNSRecord.jsm', - 'fallback/DNSResourceRecord.jsm', - 'fallback/DNSTypes.jsm', - 'fallback/MulticastDNS.jsm', -] + LOCAL_INCLUDES += [ + '/netwerk/base', + ] + +else: + EXTRA_COMPONENTS += [ + 'nsDNSServiceDiscovery.js', + 'nsDNSServiceDiscovery.manifest', + ] + + EXTRA_JS_MODULES += [ + 'fallback/DataReader.jsm', + 'fallback/DataWriter.jsm', + 'fallback/DNSPacket.jsm', + 'fallback/DNSRecord.jsm', + 'fallback/DNSResourceRecord.jsm', + 'fallback/DNSTypes.jsm', + 'fallback/MulticastDNS.jsm', + ] SOURCES += [ 'nsDNSServiceInfo.cpp', diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp new file mode 100644 index 0000000000..cec8033d18 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDNSServiceDiscovery.h" +#include "MDNSResponderOperator.h" +#include "nsICancelable.h" +#include "nsXULAppAPI.h" +#include "private/pprio.h" + +namespace mozilla { +namespace net { + +namespace { + +inline void +StartService() +{ + /*** STUB ***/ +} + +inline void +StopService() +{ + /*** STUB ***/ +} + +class ServiceCounter +{ +public: + static bool IsServiceRunning() + { + return !!sUseCount; + } + +private: + static uint32_t sUseCount; + +protected: + ServiceCounter() + { + MOZ_ASSERT(NS_IsMainThread()); + if (!sUseCount++) { + StartService(); + } + } + + virtual ~ServiceCounter() + { + MOZ_ASSERT(NS_IsMainThread()); + if (!--sUseCount) { + StopService(); + } + } +}; + +uint32_t ServiceCounter::sUseCount = 0; + +class DiscoveryRequest final : public nsICancelable + , private ServiceCounter +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICANCELABLE + + explicit DiscoveryRequest(nsDNSServiceDiscovery* aService, + nsIDNSServiceDiscoveryListener* aListener); + +private: + virtual ~DiscoveryRequest() { Cancel(NS_OK); } + + RefPtr<nsDNSServiceDiscovery> mService; + nsIDNSServiceDiscoveryListener* mListener; +}; + +NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable) + +DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService, + nsIDNSServiceDiscoveryListener* aListener) + : mService(aService) + , mListener(aListener) +{ +} + +NS_IMETHODIMP +DiscoveryRequest::Cancel(nsresult aReason) +{ + if (mService) { + mService->StopDiscovery(mListener); + } + + mService = nullptr; + return NS_OK; +} + +class RegisterRequest final : public nsICancelable + , private ServiceCounter +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICANCELABLE + + explicit RegisterRequest(nsDNSServiceDiscovery* aService, + nsIDNSRegistrationListener* aListener); + +private: + virtual ~RegisterRequest() { Cancel(NS_OK); } + + RefPtr<nsDNSServiceDiscovery> mService; + nsIDNSRegistrationListener* mListener; +}; + +NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable) + +RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService, + nsIDNSRegistrationListener* aListener) + : mService(aService) + , mListener(aListener) +{ +} + +NS_IMETHODIMP +RegisterRequest::Cancel(nsresult aReason) +{ + if (mService) { + mService->UnregisterService(mListener); + } + + mService = nullptr; + return NS_OK; +} + +} // namespace anonymous + +NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery) + +nsDNSServiceDiscovery::~nsDNSServiceDiscovery() +{ +} + +nsresult +nsDNSServiceDiscovery::Init() +{ + if (!XRE_IsParentProcess()) { + MOZ_ASSERT(false, "nsDNSServiceDiscovery can only be used in parent process"); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType, + nsIDNSServiceDiscoveryListener* aListener, + nsICancelable** aRetVal) +{ + MOZ_ASSERT(aRetVal); + + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) { + return rv; + } + + nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener); + RefPtr<BrowseOperator> browserOp = new BrowseOperator(aServiceType, + aListener); + if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) { + return rv; + } + + mDiscoveryMap.Put(aListener, browserOp); + + req.forget(aRetVal); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::StopDiscovery(nsIDNSServiceDiscoveryListener* aListener) +{ + nsresult rv; + + RefPtr<BrowseOperator> browserOp; + if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) { + return NS_OK; + } + + browserOp->Cancel(); // cancel non-started operation + if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) { + return rv; + } + + mDiscoveryMap.Remove(aListener); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo, + nsIDNSRegistrationListener* aListener, + nsICancelable** aRetVal) +{ + MOZ_ASSERT(aRetVal); + + nsresult rv; + if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) { + return rv; + } + + nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener); + RefPtr<RegisterOperator> registerOp = new RegisterOperator(aServiceInfo, + aListener); + if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) { + return rv; + } + + mRegisterMap.Put(aListener, registerOp); + + req.forget(aRetVal); + + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::UnregisterService(nsIDNSRegistrationListener* aListener) +{ + nsresult rv; + + RefPtr<RegisterOperator> registerOp; + if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) { + return NS_OK; + } + + registerOp->Cancel(); // cancel non-started operation + if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) { + return rv; + } + + mRegisterMap.Remove(aListener); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo, + nsIDNSServiceResolveListener* aListener) +{ + if (!ServiceCounter::IsServiceRunning()) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + + RefPtr<ResolveOperator> resolveOp = new ResolveOperator(aServiceInfo, + aListener); + if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) { + return rv; + } + + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h new file mode 100644 index 0000000000..abe98f3574 --- /dev/null +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h +#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h + +#include "MDNSResponderOperator.h" +#include "nsIDNSServiceDiscovery.h" +#include "nsCOMPtr.h" +#include "mozilla/RefPtr.h" +#include "nsRefPtrHashtable.h" + +namespace mozilla { +namespace net { + +class BrowseOperator; +class RegisterOperator; + +class nsDNSServiceDiscovery final : public nsIDNSServiceDiscovery +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSSERVICEDISCOVERY + + explicit nsDNSServiceDiscovery() = default; + + /* + ** The mDNS service is started on demand. If no one uses, mDNS service will not + ** start. Therefore, all operations before service started will fail + ** and get error code |kDNSServiceErr_ServiceNotRunning| defined in dns_sd.h. + **/ + nsresult Init(); + + nsresult StopDiscovery(nsIDNSServiceDiscoveryListener* aListener); + nsresult UnregisterService(nsIDNSRegistrationListener* aListener); + +private: + virtual ~nsDNSServiceDiscovery(); + + nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap; + nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h |