mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
f115503a0b
MozReview-Commit-ID: DKR6ROiHRS7
785 lines
22 KiB
C++
785 lines
22 KiB
C++
/* -*- 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(OnSocketThread(), "not on socket thread");
|
|
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(OnSocketThread(), "not on socket thread");
|
|
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("MDNSResponderOperator::ServiceWatcher::Deallocate",
|
|
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(!OnSocketThread(), "on socket thread");
|
|
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("MDNSResponderOperator::ServiceWatcher::OnMsgAttach",
|
|
&ServiceWatcher::OnMsgAttach);
|
|
}
|
|
|
|
void Close()
|
|
{
|
|
MOZ_ASSERT(!OnSocketThread(), "on socket thread");
|
|
|
|
if (!gSocketTransportService) {
|
|
Deallocate();
|
|
return;
|
|
}
|
|
|
|
PostEvent("MDNSResponderOperator::ServiceWatcher::OnMsgClose",
|
|
&ServiceWatcher::OnMsgClose);
|
|
}
|
|
|
|
private:
|
|
~ServiceWatcher() = default;
|
|
|
|
void Deallocate()
|
|
{
|
|
if (mService) {
|
|
DNSServiceRefDeallocate(mService);
|
|
mService = nullptr;
|
|
}
|
|
mOperatorHolder = nullptr;
|
|
}
|
|
|
|
nsresult PostEvent(const char* aName,
|
|
void(ServiceWatcher::*func)(void))
|
|
{
|
|
return gSocketTransportService->Dispatch(NewRunnableMethod(aName, this, func),
|
|
NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
void OnMsgClose()
|
|
{
|
|
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
|
|
|
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(OnSocketThread(), "not on socket thread");
|
|
|
|
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(OnSocketThread(), "not on socket thread");
|
|
|
|
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("MDNSResponderOperator::ServiceWatcher::OnMsgAttach",
|
|
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
|