mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 02:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
779
netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
Normal file
779
netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
Normal file
|
|
@ -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
|
||||
152
netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
Normal file
152
netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
Normal file
|
|
@ -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
|
||||
302
netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
Normal file
302
netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
Normal file
|
|
@ -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
|
||||
164
netwerk/dns/mdns/libmdns/MDNSResponderReply.h
Normal file
164
netwerk/dns/mdns/libmdns/MDNSResponderReply.h
Normal file
|
|
@ -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
|
||||
244
netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
Normal file
244
netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MulticastDNS"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
var log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MulticastDNS");
|
||||
|
||||
const FAILURE_INTERNAL_ERROR = -65537;
|
||||
|
||||
// Helper function for sending commands to Java.
|
||||
function send(type, data, callback) {
|
||||
let msg = {
|
||||
type: type
|
||||
};
|
||||
|
||||
for (let i in data) {
|
||||
try {
|
||||
msg[i] = data[i];
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
Messaging.sendRequestForResult(msg)
|
||||
.then(result => callback(result, null),
|
||||
err => callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR));
|
||||
}
|
||||
|
||||
// Receives service found/lost event from NsdManager
|
||||
function ServiceManager() {
|
||||
}
|
||||
|
||||
ServiceManager.prototype = {
|
||||
listeners: {},
|
||||
numListeners: 0,
|
||||
|
||||
registerEvent: function() {
|
||||
log("registerEvent");
|
||||
Messaging.addListener(this.onServiceFound.bind(this), "NsdManager:ServiceFound");
|
||||
Messaging.addListener(this.onServiceLost.bind(this), "NsdManager:ServiceLost");
|
||||
},
|
||||
|
||||
unregisterEvent: function() {
|
||||
log("unregisterEvent");
|
||||
Messaging.removeListener("NsdManager:ServiceFound");
|
||||
Messaging.removeListener("NsdManager:ServiceLost");
|
||||
},
|
||||
|
||||
addListener: function(aServiceType, aListener) {
|
||||
log("addListener: " + aServiceType + ", " + aListener);
|
||||
|
||||
if (!this.listeners[aServiceType]) {
|
||||
this.listeners[aServiceType] = [];
|
||||
}
|
||||
if (this.listeners[aServiceType].includes(aListener)) {
|
||||
log("listener already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
this.listeners[aServiceType].push(aListener);
|
||||
++this.numListeners;
|
||||
|
||||
if (this.numListeners === 1) {
|
||||
this.registerEvent();
|
||||
}
|
||||
|
||||
log("listener added: " + this);
|
||||
},
|
||||
|
||||
removeListener: function(aServiceType, aListener) {
|
||||
log("removeListener: " + aServiceType + ", " + aListener);
|
||||
|
||||
if (!this.listeners[aServiceType]) {
|
||||
log("listener doesn't exist");
|
||||
return;
|
||||
}
|
||||
let index = this.listeners[aServiceType].indexOf(aListener);
|
||||
if (index < 0) {
|
||||
log("listener doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
this.listeners[aServiceType].splice(index, 1);
|
||||
--this.numListeners;
|
||||
|
||||
if (this.numListeners === 0) {
|
||||
this.unregisterEvent();
|
||||
}
|
||||
|
||||
log("listener removed" + this);
|
||||
},
|
||||
|
||||
onServiceFound: function(aServiceInfo) {
|
||||
let listeners = this.listeners[aServiceInfo.serviceType];
|
||||
if (listeners) {
|
||||
for (let listener of listeners) {
|
||||
listener.onServiceFound(aServiceInfo);
|
||||
}
|
||||
} else {
|
||||
log("no listener");
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
onServiceLost: function(aServiceInfo) {
|
||||
let listeners = this.listeners[aServiceInfo.serviceType];
|
||||
if (listeners) {
|
||||
for (let listener of listeners) {
|
||||
listener.onServiceLost(aServiceInfo);
|
||||
}
|
||||
} else {
|
||||
log("no listener");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
// make an object from nsIPropertyBag2
|
||||
function parsePropertyBag2(bag) {
|
||||
if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
|
||||
throw new TypeError("Not a property bag");
|
||||
}
|
||||
|
||||
let attributes = [];
|
||||
let enumerator = bag.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let name = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
|
||||
let value = bag.getPropertyAsACString(name);
|
||||
attributes.push({
|
||||
"name": name,
|
||||
"value": value
|
||||
});
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function MulticastDNS() {
|
||||
this.serviceManager = new ServiceManager();
|
||||
}
|
||||
|
||||
MulticastDNS.prototype = {
|
||||
startDiscovery: function(aServiceType, aListener) {
|
||||
this.serviceManager.addListener(aServiceType, aListener);
|
||||
|
||||
let serviceInfo = {
|
||||
serviceType: aServiceType,
|
||||
uniqueId: aListener.uuid
|
||||
};
|
||||
|
||||
send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
|
||||
if (err) {
|
||||
log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
|
||||
this.serviceManager.removeListener(aServiceType, aListener);
|
||||
aListener.onStartDiscoveryFailed(aServiceType, err);
|
||||
} else {
|
||||
aListener.onDiscoveryStarted(result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stopDiscovery: function(aServiceType, aListener) {
|
||||
this.serviceManager.removeListener(aServiceType, aListener);
|
||||
|
||||
let serviceInfo = {
|
||||
uniqueId: aListener.uuid
|
||||
};
|
||||
|
||||
send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
|
||||
if (err) {
|
||||
log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
|
||||
aListener.onStopDiscoveryFailed(aServiceType, err);
|
||||
} else {
|
||||
aListener.onDiscoveryStopped(aServiceType);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
registerService: function(aServiceInfo, aListener) {
|
||||
let serviceInfo = {
|
||||
port: aServiceInfo.port,
|
||||
serviceType: aServiceInfo.serviceType,
|
||||
uniqueId: aListener.uuid
|
||||
};
|
||||
|
||||
try {
|
||||
serviceInfo.host = aServiceInfo.host;
|
||||
} catch(e) {
|
||||
// host unspecified
|
||||
}
|
||||
try {
|
||||
serviceInfo.serviceName = aServiceInfo.serviceName;
|
||||
} catch(e) {
|
||||
// serviceName unspecified
|
||||
}
|
||||
try {
|
||||
serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes);
|
||||
} catch(e) {
|
||||
// attributes unspecified
|
||||
}
|
||||
|
||||
send("NsdManager:RegisterService", serviceInfo, (result, err) => {
|
||||
if (err) {
|
||||
log("onRegistrationFailed: (" + err + ")");
|
||||
aListener.onRegistrationFailed(aServiceInfo, err);
|
||||
} else {
|
||||
aListener.onServiceRegistered(result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
unregisterService: function(aServiceInfo, aListener) {
|
||||
let serviceInfo = {
|
||||
uniqueId: aListener.uuid
|
||||
};
|
||||
|
||||
send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
|
||||
if (err) {
|
||||
log("onUnregistrationFailed: (" + err + ")");
|
||||
aListener.onUnregistrationFailed(aServiceInfo, err);
|
||||
} else {
|
||||
aListener.onServiceUnregistered(aServiceInfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
resolveService: function(aServiceInfo, aListener) {
|
||||
send("NsdManager:ResolveService", aServiceInfo, (result, err) => {
|
||||
if (err) {
|
||||
log("onResolveFailed: (" + err + ")");
|
||||
aListener.onResolveFailed(aServiceInfo, err);
|
||||
} else {
|
||||
aListener.onServiceResolved(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
297
netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm
Normal file
297
netwerk/dns/mdns/libmdns/fallback/DNSPacket.jsm
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['DNSPacket'];
|
||||
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
Cu.import('resource://gre/modules/DataReader.jsm');
|
||||
Cu.import('resource://gre/modules/DataWriter.jsm');
|
||||
Cu.import('resource://gre/modules/DNSRecord.jsm');
|
||||
Cu.import('resource://gre/modules/DNSResourceRecord.jsm');
|
||||
|
||||
const DEBUG = true;
|
||||
|
||||
function debug(msg) {
|
||||
Services.console.logStringMessage('DNSPacket: ' + msg);
|
||||
}
|
||||
|
||||
let DNS_PACKET_SECTION_TYPES = [
|
||||
'QD', // Question
|
||||
'AN', // Answer
|
||||
'NS', // Authority
|
||||
'AR' // Additional
|
||||
];
|
||||
|
||||
/**
|
||||
* DNS Packet Structure
|
||||
* *************************************************
|
||||
*
|
||||
* Header
|
||||
* ======
|
||||
*
|
||||
* 00 2-Bytes 15
|
||||
* -------------------------------------------------
|
||||
* |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
|
||||
* -------------------------------------------------
|
||||
* |<==================== ID =====================>|
|
||||
* |QR|<== OP ===>|AA|TC|RD|RA|UN|AD|CD|<== RC ===>|
|
||||
* |<================== QDCOUNT ==================>|
|
||||
* |<================== ANCOUNT ==================>|
|
||||
* |<================== NSCOUNT ==================>|
|
||||
* |<================== ARCOUNT ==================>|
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* ID: 2-Bytes
|
||||
* FLAGS: 2-Bytes
|
||||
* - QR: 1-Bit
|
||||
* - OP: 4-Bits
|
||||
* - AA: 1-Bit
|
||||
* - TC: 1-Bit
|
||||
* - RD: 1-Bit
|
||||
* - RA: 1-Bit
|
||||
* - UN: 1-Bit
|
||||
* - AD: 1-Bit
|
||||
* - CD: 1-Bit
|
||||
* - RC: 4-Bits
|
||||
* QDCOUNT: 2-Bytes
|
||||
* ANCOUNT: 2-Bytes
|
||||
* NSCOUNT: 2-Bytes
|
||||
* ARCOUNT: 2-Bytes
|
||||
*
|
||||
*
|
||||
* Data
|
||||
* ====
|
||||
*
|
||||
* 00 2-Bytes 15
|
||||
* -------------------------------------------------
|
||||
* |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
|
||||
* -------------------------------------------------
|
||||
* |<???=============== QD[...] ===============???>|
|
||||
* |<???=============== AN[...] ===============???>|
|
||||
* |<???=============== NS[...] ===============???>|
|
||||
* |<???=============== AR[...] ===============???>|
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* QD: ??-Bytes
|
||||
* AN: ??-Bytes
|
||||
* NS: ??-Bytes
|
||||
* AR: ??-Bytes
|
||||
*
|
||||
*
|
||||
* Question Record
|
||||
* ===============
|
||||
*
|
||||
* 00 2-Bytes 15
|
||||
* -------------------------------------------------
|
||||
* |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|
|
||||
* -------------------------------------------------
|
||||
* |<???================ NAME =================???>|
|
||||
* |<=================== TYPE ====================>|
|
||||
* |<=================== CLASS ===================>|
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* NAME: ??-Bytes
|
||||
* TYPE: 2-Bytes
|
||||
* CLASS: 2-Bytes
|
||||
*
|
||||
*
|
||||
* Resource Record
|
||||
* ===============
|
||||
*
|
||||
* 00 4-Bytes 31
|
||||
* -------------------------------------------------
|
||||
* |00|02|04|06|08|10|12|14|16|18|20|22|24|26|28|30|
|
||||
* -------------------------------------------------
|
||||
* |<???================ NAME =================???>|
|
||||
* |<======= TYPE ========>|<======= CLASS =======>|
|
||||
* |<==================== TTL ====================>|
|
||||
* |<====== DATALEN ======>|<???==== DATA =====???>|
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* NAME: ??-Bytes
|
||||
* TYPE: 2-Bytes
|
||||
* CLASS: 2-Bytes
|
||||
* DATALEN: 2-Bytes
|
||||
* DATA: ??-Bytes (Specified By DATALEN)
|
||||
*/
|
||||
class DNSPacket {
|
||||
constructor() {
|
||||
this._flags = _valueToFlags(0x0000);
|
||||
this._records = {};
|
||||
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
this._records[sectionType] = [];
|
||||
});
|
||||
}
|
||||
|
||||
static parse(data) {
|
||||
let reader = new DataReader(data);
|
||||
if (reader.getValue(2) !== 0x0000) {
|
||||
throw new Error('Packet must start with 0x0000');
|
||||
}
|
||||
|
||||
let packet = new DNSPacket();
|
||||
packet._flags = _valueToFlags(reader.getValue(2));
|
||||
|
||||
let recordCounts = {};
|
||||
|
||||
// Parse the record counts.
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
recordCounts[sectionType] = reader.getValue(2);
|
||||
});
|
||||
|
||||
// Parse the actual records.
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
let recordCount = recordCounts[sectionType];
|
||||
for (let i = 0; i < recordCount; i++) {
|
||||
if (sectionType === 'QD') {
|
||||
packet.addRecord(sectionType,
|
||||
DNSRecord.parseFromPacketReader(reader));
|
||||
}
|
||||
|
||||
else {
|
||||
packet.addRecord(sectionType,
|
||||
DNSResourceRecord.parseFromPacketReader(reader));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!reader.eof) {
|
||||
DEBUG && debug('Did not complete parsing packet data');
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
getFlag(flag) {
|
||||
return this._flags[flag];
|
||||
}
|
||||
|
||||
setFlag(flag, value) {
|
||||
this._flags[flag] = value;
|
||||
}
|
||||
|
||||
addRecord(sectionType, record) {
|
||||
this._records[sectionType].push(record);
|
||||
}
|
||||
|
||||
getRecords(sectionTypes, recordType) {
|
||||
let records = [];
|
||||
|
||||
sectionTypes.forEach((sectionType) => {
|
||||
records = records.concat(this._records[sectionType]);
|
||||
});
|
||||
|
||||
if (!recordType) {
|
||||
return records;
|
||||
}
|
||||
|
||||
return records.filter(r => r.recordType === recordType);
|
||||
}
|
||||
|
||||
serialize() {
|
||||
let writer = new DataWriter();
|
||||
|
||||
// Write leading 0x0000 (2 bytes)
|
||||
writer.putValue(0x0000, 2);
|
||||
|
||||
// Write `flags` (2 bytes)
|
||||
writer.putValue(_flagsToValue(this._flags), 2);
|
||||
|
||||
// Write lengths of record sections (2 bytes each)
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
writer.putValue(this._records[sectionType].length, 2);
|
||||
});
|
||||
|
||||
// Write records
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
this._records[sectionType].forEach((record) => {
|
||||
writer.putBytes(record.serialize());
|
||||
});
|
||||
});
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return JSON.stringify(this.toJSONObject());
|
||||
}
|
||||
|
||||
toJSONObject() {
|
||||
let result = {flags: this._flags};
|
||||
DNS_PACKET_SECTION_TYPES.forEach((sectionType) => {
|
||||
result[sectionType] = [];
|
||||
|
||||
let records = this._records[sectionType];
|
||||
records.forEach((record) => {
|
||||
result[sectionType].push(record.toJSONObject());
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _valueToFlags(value) {
|
||||
return {
|
||||
QR: (value & 0x8000) >> 15,
|
||||
OP: (value & 0x7800) >> 11,
|
||||
AA: (value & 0x0400) >> 10,
|
||||
TC: (value & 0x0200) >> 9,
|
||||
RD: (value & 0x0100) >> 8,
|
||||
RA: (value & 0x0080) >> 7,
|
||||
UN: (value & 0x0040) >> 6,
|
||||
AD: (value & 0x0020) >> 5,
|
||||
CD: (value & 0x0010) >> 4,
|
||||
RC: (value & 0x000f) >> 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _flagsToValue(flags) {
|
||||
let value = 0x0000;
|
||||
|
||||
value += flags.QR & 0x01;
|
||||
|
||||
value <<= 4;
|
||||
value += flags.OP & 0x0f;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.AA & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.TC & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.RD & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.RA & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.UN & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.AD & 0x01;
|
||||
|
||||
value <<= 1;
|
||||
value += flags.CD & 0x01;
|
||||
|
||||
value <<= 4;
|
||||
value += flags.RC & 0x0f;
|
||||
|
||||
return value;
|
||||
}
|
||||
70
netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm
Normal file
70
netwerk/dns/mdns/libmdns/fallback/DNSRecord.jsm
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['DNSRecord'];
|
||||
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/DataWriter.jsm');
|
||||
Cu.import('resource://gre/modules/DNSTypes.jsm');
|
||||
|
||||
class DNSRecord {
|
||||
constructor(properties = {}) {
|
||||
this.name = properties.name || '';
|
||||
this.recordType = properties.recordType || DNS_RECORD_TYPES.ANY;
|
||||
this.classCode = properties.classCode || DNS_CLASS_CODES.IN;
|
||||
this.cacheFlush = properties.cacheFlush || false;
|
||||
}
|
||||
|
||||
static parseFromPacketReader(reader) {
|
||||
let name = reader.getLabel();
|
||||
let recordType = reader.getValue(2);
|
||||
let classCode = reader.getValue(2);
|
||||
let cacheFlush = (classCode & 0x8000) ? true : false;
|
||||
classCode &= 0xff;
|
||||
|
||||
return new this({
|
||||
name: name,
|
||||
recordType: recordType,
|
||||
classCode: classCode,
|
||||
cacheFlush: cacheFlush
|
||||
});
|
||||
}
|
||||
|
||||
serialize() {
|
||||
let writer = new DataWriter();
|
||||
|
||||
// Write `name` (ends with trailing 0x00 byte)
|
||||
writer.putLabel(this.name);
|
||||
|
||||
// Write `recordType` (2 bytes)
|
||||
writer.putValue(this.recordType, 2);
|
||||
|
||||
// Write `classCode` (2 bytes)
|
||||
let classCode = this.classCode;
|
||||
if (this.cacheFlush) {
|
||||
classCode |= 0x8000;
|
||||
}
|
||||
writer.putValue(classCode, 2);
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return JSON.stringify(this.toJSONObject());
|
||||
}
|
||||
|
||||
toJSONObject() {
|
||||
return {
|
||||
name: this.name,
|
||||
recordType: this.recordType,
|
||||
classCode: this.classCode,
|
||||
cacheFlush: this.cacheFlush
|
||||
};
|
||||
}
|
||||
}
|
||||
221
netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm
Normal file
221
netwerk/dns/mdns/libmdns/fallback/DNSResourceRecord.jsm
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['DNSResourceRecord'];
|
||||
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/DataReader.jsm');
|
||||
Cu.import('resource://gre/modules/DataWriter.jsm');
|
||||
Cu.import('resource://gre/modules/DNSRecord.jsm');
|
||||
Cu.import('resource://gre/modules/DNSTypes.jsm');
|
||||
|
||||
function debug(msg) {
|
||||
Services.console.logStringMessage('MulticastDNS: ' + msg);
|
||||
}
|
||||
|
||||
const DNS_RESOURCE_RECORD_DEFAULT_TTL = 120; // 120 seconds
|
||||
|
||||
class DNSResourceRecord extends DNSRecord {
|
||||
constructor(properties = {}) {
|
||||
super(properties);
|
||||
|
||||
this.ttl = properties.ttl || DNS_RESOURCE_RECORD_DEFAULT_TTL;
|
||||
this.data = properties.data || {};
|
||||
}
|
||||
|
||||
static parseFromPacketReader(reader) {
|
||||
let record = super.parseFromPacketReader(reader);
|
||||
|
||||
let ttl = reader.getValue(4);
|
||||
let recordData = reader.getBytes(reader.getValue(2));
|
||||
let packetData = reader.data;
|
||||
|
||||
let data;
|
||||
|
||||
switch (record.recordType) {
|
||||
case DNS_RECORD_TYPES.A:
|
||||
data = _parseA(recordData, packetData);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.PTR:
|
||||
data = _parsePTR(recordData, packetData);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.TXT:
|
||||
data = _parseTXT(recordData, packetData);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.SRV:
|
||||
data = _parseSRV(recordData, packetData);
|
||||
break;
|
||||
default:
|
||||
data = null;
|
||||
break;
|
||||
}
|
||||
|
||||
record.ttl = ttl;
|
||||
record.data = data;
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
let writer = new DataWriter(super.serialize());
|
||||
|
||||
// Write `ttl` (4 bytes)
|
||||
writer.putValue(this.ttl, 4);
|
||||
|
||||
let data;
|
||||
|
||||
switch (this.recordType) {
|
||||
case DNS_RECORD_TYPES.A:
|
||||
data = _serializeA(this.data);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.PTR:
|
||||
data = _serializePTR(this.data);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.TXT:
|
||||
data = _serializeTXT(this.data);
|
||||
break;
|
||||
case DNS_RECORD_TYPES.SRV:
|
||||
data = _serializeSRV(this.data);
|
||||
break;
|
||||
default:
|
||||
data = new Uint8Array();
|
||||
break;
|
||||
}
|
||||
|
||||
// Write `data` length.
|
||||
writer.putValue(data.length, 2);
|
||||
|
||||
// Write `data` (ends with trailing 0x00 byte)
|
||||
writer.putBytes(data);
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return JSON.stringify(this.toJSONObject());
|
||||
}
|
||||
|
||||
toJSONObject() {
|
||||
let result = super.toJSONObject();
|
||||
result.ttl = this.ttl;
|
||||
result.data = this.data;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _parseA(recordData, packetData) {
|
||||
let reader = new DataReader(recordData);
|
||||
|
||||
let parts = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
parts.push(reader.getValue(1));
|
||||
}
|
||||
|
||||
return parts.join('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _parsePTR(recordData, packetData) {
|
||||
let reader = new DataReader(recordData);
|
||||
|
||||
return reader.getLabel(packetData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _parseTXT(recordData, packetData) {
|
||||
let reader = new DataReader(recordData);
|
||||
|
||||
let result = {};
|
||||
|
||||
let label = reader.getLabel(packetData);
|
||||
if (label.length > 0) {
|
||||
let parts = label.split('.');
|
||||
parts.forEach((part) => {
|
||||
let [name] = part.split('=', 1);
|
||||
let value = part.substr(name.length + 1);
|
||||
result[name] = value;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _parseSRV(recordData, packetData) {
|
||||
let reader = new DataReader(recordData);
|
||||
|
||||
let priority = reader.getValue(2);
|
||||
let weight = reader.getValue(2);
|
||||
let port = reader.getValue(2);
|
||||
let target = reader.getLabel(packetData);
|
||||
|
||||
return { priority, weight, port, target };
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _serializeA(data) {
|
||||
let writer = new DataWriter();
|
||||
|
||||
let parts = data.split('.');
|
||||
for (let i = 0; i < 4; i++) {
|
||||
writer.putValue(parseInt(parts[i], 10) || 0);
|
||||
}
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _serializePTR(data) {
|
||||
let writer = new DataWriter();
|
||||
|
||||
writer.putLabel(data);
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _serializeTXT(data) {
|
||||
let writer = new DataWriter();
|
||||
|
||||
for (let name in data) {
|
||||
writer.putLengthString(name + '=' + data[name]);
|
||||
}
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _serializeSRV(data) {
|
||||
let writer = new DataWriter();
|
||||
|
||||
writer.putValue(data.priority || 0, 2);
|
||||
writer.putValue(data.weight || 0, 2);
|
||||
writer.putValue(data.port || 0, 2);
|
||||
writer.putLabel(data.target);
|
||||
|
||||
return writer.data;
|
||||
}
|
||||
100
netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm
Normal file
100
netwerk/dns/mdns/libmdns/fallback/DNSTypes.jsm
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
'DNS_QUERY_RESPONSE_CODES',
|
||||
'DNS_AUTHORITATIVE_ANSWER_CODES',
|
||||
'DNS_CLASS_CODES',
|
||||
'DNS_RECORD_TYPES'
|
||||
];
|
||||
|
||||
let DNS_QUERY_RESPONSE_CODES = {
|
||||
QUERY : 0, // RFC 1035 - Query
|
||||
RESPONSE : 1 // RFC 1035 - Reponse
|
||||
};
|
||||
|
||||
let DNS_AUTHORITATIVE_ANSWER_CODES = {
|
||||
NO : 0, // RFC 1035 - Not Authoritative
|
||||
YES : 1 // RFC 1035 - Is Authoritative
|
||||
};
|
||||
|
||||
let DNS_CLASS_CODES = {
|
||||
IN : 0x01, // RFC 1035 - Internet
|
||||
CS : 0x02, // RFC 1035 - CSNET
|
||||
CH : 0x03, // RFC 1035 - CHAOS
|
||||
HS : 0x04, // RFC 1035 - Hesiod
|
||||
NONE : 0xfe, // RFC 2136 - None
|
||||
ANY : 0xff, // RFC 1035 - Any
|
||||
};
|
||||
|
||||
let DNS_RECORD_TYPES = {
|
||||
SIGZERO : 0, // RFC 2931
|
||||
A : 1, // RFC 1035
|
||||
NS : 2, // RFC 1035
|
||||
MD : 3, // RFC 1035
|
||||
MF : 4, // RFC 1035
|
||||
CNAME : 5, // RFC 1035
|
||||
SOA : 6, // RFC 1035
|
||||
MB : 7, // RFC 1035
|
||||
MG : 8, // RFC 1035
|
||||
MR : 9, // RFC 1035
|
||||
NULL : 10, // RFC 1035
|
||||
WKS : 11, // RFC 1035
|
||||
PTR : 12, // RFC 1035
|
||||
HINFO : 13, // RFC 1035
|
||||
MINFO : 14, // RFC 1035
|
||||
MX : 15, // RFC 1035
|
||||
TXT : 16, // RFC 1035
|
||||
RP : 17, // RFC 1183
|
||||
AFSDB : 18, // RFC 1183
|
||||
X25 : 19, // RFC 1183
|
||||
ISDN : 20, // RFC 1183
|
||||
RT : 21, // RFC 1183
|
||||
NSAP : 22, // RFC 1706
|
||||
NSAP_PTR : 23, // RFC 1348
|
||||
SIG : 24, // RFC 2535
|
||||
KEY : 25, // RFC 2535
|
||||
PX : 26, // RFC 2163
|
||||
GPOS : 27, // RFC 1712
|
||||
AAAA : 28, // RFC 1886
|
||||
LOC : 29, // RFC 1876
|
||||
NXT : 30, // RFC 2535
|
||||
EID : 31, // RFC ????
|
||||
NIMLOC : 32, // RFC ????
|
||||
SRV : 33, // RFC 2052
|
||||
ATMA : 34, // RFC ????
|
||||
NAPTR : 35, // RFC 2168
|
||||
KX : 36, // RFC 2230
|
||||
CERT : 37, // RFC 2538
|
||||
DNAME : 39, // RFC 2672
|
||||
OPT : 41, // RFC 2671
|
||||
APL : 42, // RFC 3123
|
||||
DS : 43, // RFC 4034
|
||||
SSHFP : 44, // RFC 4255
|
||||
IPSECKEY : 45, // RFC 4025
|
||||
RRSIG : 46, // RFC 4034
|
||||
NSEC : 47, // RFC 4034
|
||||
DNSKEY : 48, // RFC 4034
|
||||
DHCID : 49, // RFC 4701
|
||||
NSEC3 : 50, // RFC ????
|
||||
NSEC3PARAM : 51, // RFC ????
|
||||
HIP : 55, // RFC 5205
|
||||
SPF : 99, // RFC 4408
|
||||
UINFO : 100, // RFC ????
|
||||
UID : 101, // RFC ????
|
||||
GID : 102, // RFC ????
|
||||
UNSPEC : 103, // RFC ????
|
||||
TKEY : 249, // RFC 2930
|
||||
TSIG : 250, // RFC 2931
|
||||
IXFR : 251, // RFC 1995
|
||||
AXFR : 252, // RFC 1035
|
||||
MAILB : 253, // RFC 1035
|
||||
MAILA : 254, // RFC 1035
|
||||
ANY : 255, // RFC 1035
|
||||
DLV : 32769 // RFC 4431
|
||||
};
|
||||
133
netwerk/dns/mdns/libmdns/fallback/DataReader.jsm
Normal file
133
netwerk/dns/mdns/libmdns/fallback/DataReader.jsm
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['DataReader'];
|
||||
|
||||
class DataReader {
|
||||
// `data` is `Uint8Array`
|
||||
constructor(data, startByte = 0) {
|
||||
this._data = data;
|
||||
this._cursor = startByte;
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
return this._data.buffer;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get eof() {
|
||||
return this._cursor >= this._data.length;
|
||||
}
|
||||
|
||||
getBytes(length = 1) {
|
||||
if (!length) {
|
||||
return new Uint8Array();
|
||||
}
|
||||
|
||||
let end = this._cursor + length;
|
||||
if (end > this._data.length) {
|
||||
return new Uint8Array();
|
||||
}
|
||||
|
||||
let uint8Array = new Uint8Array(this.buffer.slice(this._cursor, end));
|
||||
this._cursor += length;
|
||||
|
||||
return uint8Array;
|
||||
}
|
||||
|
||||
getString(length) {
|
||||
let uint8Array = this.getBytes(length);
|
||||
return _uint8ArrayToString(uint8Array);
|
||||
}
|
||||
|
||||
getValue(length) {
|
||||
let uint8Array = this.getBytes(length);
|
||||
return _uint8ArrayToValue(uint8Array);
|
||||
}
|
||||
|
||||
getLabel(decompressData) {
|
||||
let parts = [];
|
||||
let partLength;
|
||||
|
||||
while ((partLength = this.getValue(1))) {
|
||||
// If a length has been specified instead of a pointer,
|
||||
// read the string of the specified length.
|
||||
if (partLength !== 0xc0) {
|
||||
parts.push(this.getString(partLength));
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Handle case where we have a pointer to the label
|
||||
parts.push(String.fromCharCode(0xc0) + this.getString(1));
|
||||
break;
|
||||
}
|
||||
|
||||
let label = parts.join('.');
|
||||
|
||||
return _decompressLabel(label, decompressData || this._data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _uint8ArrayToValue(uint8Array) {
|
||||
let length = uint8Array.length;
|
||||
if (length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let value = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
value = value << 8;
|
||||
value += uint8Array[i];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _uint8ArrayToString(uint8Array) {
|
||||
let length = uint8Array.length;
|
||||
if (length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let results = [];
|
||||
for (let i = 0; i < length; i += 1024) {
|
||||
results.push(String.fromCharCode.apply(null, uint8Array.subarray(i, i + 1024)));
|
||||
}
|
||||
|
||||
return results.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _decompressLabel(label, decompressData) {
|
||||
let result = '';
|
||||
|
||||
for (let i = 0, length = label.length; i < length; i++) {
|
||||
if (label.charCodeAt(i) !== 0xc0) {
|
||||
result += label.charAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
let reader = new DataReader(decompressData, label.charCodeAt(i));
|
||||
result += _decompressLabel(reader.getLabel(), decompressData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
98
netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm
Normal file
98
netwerk/dns/mdns/libmdns/fallback/DataWriter.jsm
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['DataWriter'];
|
||||
|
||||
class DataWriter {
|
||||
constructor(data, maxBytes = 512) {
|
||||
if (typeof data === 'number') {
|
||||
maxBytes = data;
|
||||
data = undefined;
|
||||
}
|
||||
|
||||
this._buffer = new ArrayBuffer(maxBytes);
|
||||
this._data = new Uint8Array(this._buffer);
|
||||
this._cursor = 0;
|
||||
|
||||
if (data) {
|
||||
this.putBytes(data);
|
||||
}
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
return this._buffer.slice(0, this._cursor);
|
||||
}
|
||||
|
||||
get data() {
|
||||
return new Uint8Array(this.buffer);
|
||||
}
|
||||
|
||||
// `data` is `Uint8Array`
|
||||
putBytes(data) {
|
||||
if (this._cursor + data.length > this._data.length) {
|
||||
throw new Error('DataWriter buffer is exceeded');
|
||||
}
|
||||
|
||||
for (let i = 0, length = data.length; i < length; i++) {
|
||||
this._data[this._cursor] = data[i];
|
||||
this._cursor++;
|
||||
}
|
||||
}
|
||||
|
||||
putByte(byte) {
|
||||
if (this._cursor + 1 > this._data.length) {
|
||||
throw new Error('DataWriter buffer is exceeded');
|
||||
}
|
||||
|
||||
this._data[this._cursor] = byte
|
||||
this._cursor++;
|
||||
}
|
||||
|
||||
putValue(value, length) {
|
||||
length = length || 1;
|
||||
if (length == 1) {
|
||||
this.putByte(value);
|
||||
} else {
|
||||
this.putBytes(_valueToUint8Array(value, length));
|
||||
}
|
||||
}
|
||||
|
||||
putLabel(label) {
|
||||
// Eliminate any trailing '.'s in the label (valid in text representation).
|
||||
label = label.replace(/\.$/, '');
|
||||
let parts = label.split('.');
|
||||
parts.forEach((part) => {
|
||||
this.putLengthString(part);
|
||||
});
|
||||
this.putValue(0);
|
||||
}
|
||||
|
||||
putLengthString(string) {
|
||||
if (string.length > 0xff) {
|
||||
throw new Error("String too long.");
|
||||
}
|
||||
this.putValue(string.length);
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
this.putValue(string.charCodeAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _valueToUint8Array(value, length) {
|
||||
let arrayBuffer = new ArrayBuffer(length);
|
||||
let uint8Array = new Uint8Array(arrayBuffer);
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
uint8Array[i] = value & 0xff;
|
||||
value = value >> 8;
|
||||
}
|
||||
|
||||
return uint8Array;
|
||||
}
|
||||
875
netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
Normal file
875
netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
Normal file
|
|
@ -0,0 +1,875 @@
|
|||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
/* jshint esnext: true, moz: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['MulticastDNS'];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/Timer.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
Cu.import('resource://gre/modules/DNSPacket.jsm');
|
||||
Cu.import('resource://gre/modules/DNSRecord.jsm');
|
||||
Cu.import('resource://gre/modules/DNSResourceRecord.jsm');
|
||||
Cu.import('resource://gre/modules/DNSTypes.jsm');
|
||||
|
||||
const NS_NETWORK_LINK_TOPIC = 'network:link-status-changed';
|
||||
|
||||
let observerService = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
let networkInfoService = Cc['@mozilla.org/network-info-service;1']
|
||||
.createInstance(Ci.nsINetworkInfoService);
|
||||
|
||||
const DEBUG = true;
|
||||
|
||||
const MDNS_MULTICAST_GROUP = '224.0.0.251';
|
||||
const MDNS_PORT = 5353;
|
||||
const DEFAULT_TTL = 120;
|
||||
|
||||
function debug(msg) {
|
||||
dump('MulticastDNS: ' + msg + '\n');
|
||||
}
|
||||
|
||||
function ServiceKey(svc) {
|
||||
return "" + svc.serviceType.length + "/" + svc.serviceType + "|" +
|
||||
svc.serviceName.length + "/" + svc.serviceName + "|" +
|
||||
svc.port;
|
||||
}
|
||||
|
||||
function TryGet(obj, name) {
|
||||
try {
|
||||
return obj[name];
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function IsIpv4Address(addr) {
|
||||
let parts = addr.split('.');
|
||||
if (parts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
for (let part of parts) {
|
||||
let partInt = Number.parseInt(part, 10);
|
||||
if (partInt.toString() != part) {
|
||||
return false;
|
||||
}
|
||||
if (partInt < 0 || partInt >= 256) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class PublishedService {
|
||||
constructor(attrs) {
|
||||
this.serviceType = attrs.serviceType.replace(/\.$/, '');
|
||||
this.serviceName = attrs.serviceName;
|
||||
this.domainName = TryGet(attrs, 'domainName') || "local";
|
||||
this.address = TryGet(attrs, 'address') || "0.0.0.0";
|
||||
this.port = attrs.port;
|
||||
this.serviceAttrs = _propertyBagToObject(TryGet(attrs, 'attributes') || {});
|
||||
this.host = TryGet(attrs, 'host');
|
||||
this.key = this.generateKey();
|
||||
this.lastAdvertised = undefined;
|
||||
this.advertiseTimer = undefined;
|
||||
}
|
||||
|
||||
equals(svc) {
|
||||
return (this.port == svc.port) &&
|
||||
(this.serviceName == svc.serviceName) &&
|
||||
(this.serviceType == svc.serviceType);
|
||||
}
|
||||
|
||||
generateKey() {
|
||||
return ServiceKey(this);
|
||||
}
|
||||
|
||||
ptrMatch(name) {
|
||||
return name == (this.serviceType + "." + this.domainName);
|
||||
}
|
||||
|
||||
clearAdvertiseTimer() {
|
||||
if (!this.advertiseTimer) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.advertiseTimer);
|
||||
this.advertiseTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class MulticastDNS {
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
this._sockets = new Map();
|
||||
this._services = new Map();
|
||||
this._discovered = new Map();
|
||||
this._querySocket = undefined;
|
||||
this._broadcastReceiverSocket = undefined;
|
||||
this._broadcastTimer = undefined;
|
||||
|
||||
this._networkLinkObserver = {
|
||||
observe: (subject, topic, data) => {
|
||||
DEBUG && debug(NS_NETWORK_LINK_TOPIC + '(' + data + '); Clearing list of previously discovered services');
|
||||
this._discovered.clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_attachNetworkLinkObserver() {
|
||||
if (this._networkLinkObserverTimeout) {
|
||||
clearTimeout(this._networkLinkObserverTimeout);
|
||||
}
|
||||
|
||||
if (!this._isNetworkLinkObserverAttached) {
|
||||
DEBUG && debug('Attaching observer ' + NS_NETWORK_LINK_TOPIC);
|
||||
observerService.addObserver(this._networkLinkObserver, NS_NETWORK_LINK_TOPIC, false);
|
||||
this._isNetworkLinkObserverAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
_detachNetworkLinkObserver() {
|
||||
if (this._isNetworkLinkObserverAttached) {
|
||||
if (this._networkLinkObserverTimeout) {
|
||||
clearTimeout(this._networkLinkObserverTimeout);
|
||||
}
|
||||
|
||||
this._networkLinkObserverTimeout = setTimeout(() => {
|
||||
DEBUG && debug('Detaching observer ' + NS_NETWORK_LINK_TOPIC);
|
||||
observerService.removeObserver(this._networkLinkObserver, NS_NETWORK_LINK_TOPIC);
|
||||
this._isNetworkLinkObserverAttached = false;
|
||||
this._networkLinkObserverTimeout = null;
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
startDiscovery(aServiceType, aListener) {
|
||||
DEBUG && debug('startDiscovery("' + aServiceType + '")');
|
||||
let { serviceType } = _parseServiceDomainName(aServiceType);
|
||||
|
||||
this._attachNetworkLinkObserver();
|
||||
this._addServiceListener(serviceType, aListener);
|
||||
|
||||
try {
|
||||
this._query(serviceType + '.local');
|
||||
aListener.onDiscoveryStarted(serviceType);
|
||||
} catch (e) {
|
||||
DEBUG && debug('startDiscovery("' + serviceType + '") FAILED: ' + e);
|
||||
this._removeServiceListener(serviceType, aListener);
|
||||
aListener.onStartDiscoveryFailed(serviceType, Cr.NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
stopDiscovery(aServiceType, aListener) {
|
||||
DEBUG && debug('stopDiscovery("' + aServiceType + '")');
|
||||
let { serviceType } = _parseServiceDomainName(aServiceType);
|
||||
|
||||
this._detachNetworkLinkObserver();
|
||||
this._removeServiceListener(serviceType, aListener);
|
||||
|
||||
aListener.onDiscoveryStopped(serviceType);
|
||||
|
||||
this._checkCloseSockets();
|
||||
}
|
||||
|
||||
resolveService(aServiceInfo, aListener) {
|
||||
DEBUG && debug('resolveService(): ' + aServiceInfo.serviceName);
|
||||
|
||||
// Address info is already resolved during discovery
|
||||
setTimeout(() => aListener.onServiceResolved(aServiceInfo));
|
||||
}
|
||||
|
||||
registerService(aServiceInfo, aListener) {
|
||||
DEBUG && debug('registerService(): ' + aServiceInfo.serviceName);
|
||||
|
||||
// Initialize the broadcast receiver socket in case it
|
||||
// hasn't already been started so we can listen for
|
||||
// multicast queries/announcements on all interfaces.
|
||||
this._getBroadcastReceiverSocket();
|
||||
|
||||
for (let name of ['port', 'serviceName', 'serviceType']) {
|
||||
if (!TryGet(aServiceInfo, name)) {
|
||||
aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE);
|
||||
throw new Error('Invalid nsIDNSServiceInfo; Missing "' + name + '"');
|
||||
}
|
||||
}
|
||||
|
||||
let publishedService;
|
||||
try {
|
||||
publishedService = new PublishedService(aServiceInfo);
|
||||
} catch (e) {
|
||||
DEBUG && debug("Error constructing PublishedService: " + e + " - " + e.stack);
|
||||
setTimeout(() => aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure such a service does not already exist.
|
||||
if (this._services.get(publishedService.key)) {
|
||||
setTimeout(() => aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure that the service addr is '0.0.0.0', or there is at least one
|
||||
// socket open on the address the service is open on.
|
||||
this._getSockets().then((sockets) => {
|
||||
if (publishedService.address != '0.0.0.0' && !sockets.get(publishedService.address)) {
|
||||
setTimeout(() => aListener.onRegistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
this._services.set(publishedService.key, publishedService);
|
||||
|
||||
// Service registered.. call onServiceRegistered on next tick.
|
||||
setTimeout(() => aListener.onServiceRegistered(aServiceInfo));
|
||||
|
||||
// Set a timeout to start advertising the service too.
|
||||
publishedService.advertiseTimer = setTimeout(() => {
|
||||
this._advertiseService(publishedService.key, /* firstAdv = */ true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
unregisterService(aServiceInfo, aListener) {
|
||||
DEBUG && debug('unregisterService(): ' + aServiceInfo.serviceName);
|
||||
|
||||
let serviceKey;
|
||||
try {
|
||||
serviceKey = ServiceKey(aServiceInfo);
|
||||
} catch (e) {
|
||||
setTimeout(() => aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
let publishedService = this._services.get(serviceKey);
|
||||
if (!publishedService) {
|
||||
setTimeout(() => aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any advertise timeout for this published service.
|
||||
publishedService.clearAdvertiseTimer();
|
||||
|
||||
// Delete the service from the service map.
|
||||
if (!this._services.delete(serviceKey)) {
|
||||
setTimeout(() => aListener.onUnregistrationFailed(aServiceInfo, Cr.NS_ERROR_FAILURE));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the broadcast timer again to rejig when it should run next.
|
||||
this._checkStartBroadcastTimer();
|
||||
|
||||
// Check to see if sockets should be closed, and if so close them.
|
||||
this._checkCloseSockets();
|
||||
|
||||
aListener.onServiceUnregistered(aServiceInfo);
|
||||
}
|
||||
|
||||
_respondToQuery(serviceKey, message) {
|
||||
let address = message.fromAddr.address;
|
||||
let port = message.fromAddr.port;
|
||||
DEBUG && debug('_respondToQuery(): key=' + serviceKey + ', fromAddr='
|
||||
+ address + ":" + port);
|
||||
|
||||
let publishedService = this._services.get(serviceKey);
|
||||
if (!publishedService) {
|
||||
debug("_respondToQuery Could not find service (key=" + serviceKey + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG && debug('_respondToQuery(): key=' + serviceKey + ': SENDING RESPONSE');
|
||||
this._advertiseServiceHelper(publishedService, {address,port});
|
||||
}
|
||||
|
||||
_advertiseService(serviceKey, firstAdv) {
|
||||
DEBUG && debug('_advertiseService(): key=' + serviceKey);
|
||||
let publishedService = this._services.get(serviceKey);
|
||||
if (!publishedService) {
|
||||
debug("_advertiseService Could not find service to advertise (key=" + serviceKey + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
publishedService.advertiseTimer = undefined;
|
||||
|
||||
this._advertiseServiceHelper(publishedService, null).then(() => {
|
||||
// If first advertisement, re-advertise in 1 second.
|
||||
// Otherwise, set the lastAdvertised time.
|
||||
if (firstAdv) {
|
||||
publishedService.advertiseTimer = setTimeout(() => {
|
||||
this._advertiseService(serviceKey)
|
||||
}, 1000);
|
||||
} else {
|
||||
publishedService.lastAdvertised = Date.now();
|
||||
this._checkStartBroadcastTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_advertiseServiceHelper(svc, target) {
|
||||
if (!target) {
|
||||
target = {address:MDNS_MULTICAST_GROUP, port:MDNS_PORT};
|
||||
}
|
||||
|
||||
return this._getSockets().then((sockets) => {
|
||||
sockets.forEach((socket, address) => {
|
||||
if (svc.address == "0.0.0.0" || address == svc.address)
|
||||
{
|
||||
let packet = this._makeServicePacket(svc, [address]);
|
||||
let data = packet.serialize();
|
||||
try {
|
||||
socket.send(target.address, target.port, data, data.length);
|
||||
} catch (err) {
|
||||
DEBUG && debug("Failed to send packet to "
|
||||
+ target.address + ":" + target.port);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_cancelBroadcastTimer() {
|
||||
if (!this._broadcastTimer) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this._broadcastTimer);
|
||||
this._broadcastTimer = undefined;
|
||||
}
|
||||
|
||||
_checkStartBroadcastTimer() {
|
||||
DEBUG && debug("_checkStartBroadcastTimer()");
|
||||
// Cancel any existing broadcasting timer.
|
||||
this._cancelBroadcastTimer();
|
||||
|
||||
let now = Date.now();
|
||||
|
||||
// Go through services and find services to broadcast.
|
||||
let bcastServices = [];
|
||||
let nextBcastWait = undefined;
|
||||
for (let [serviceKey, publishedService] of this._services) {
|
||||
// if lastAdvertised is undefined, service hasn't finished it's initial
|
||||
// two broadcasts.
|
||||
if (publishedService.lastAdvertised === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, check lastAdvertised against now.
|
||||
let msSinceAdv = now - publishedService.lastAdvertised;
|
||||
|
||||
// If msSinceAdv is more than 90% of the way to the TTL, advertise now.
|
||||
if (msSinceAdv > (DEFAULT_TTL * 1000 * 0.9)) {
|
||||
bcastServices.push(publishedService);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, calculate the next time to advertise for this service.
|
||||
// We set that at 95% of the time to the TTL expiry.
|
||||
let nextAdvWait = (DEFAULT_TTL * 1000 * 0.95) - msSinceAdv;
|
||||
if (nextBcastWait === undefined || nextBcastWait > nextAdvWait) {
|
||||
nextBcastWait = nextAdvWait;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule an immediate advertisement of all services to be advertised now.
|
||||
for (let svc of bcastServices) {
|
||||
svc.advertiseTimer = setTimeout(() => this._advertiseService(svc.key));
|
||||
}
|
||||
|
||||
// Schedule next broadcast check for the next bcast time.
|
||||
if (nextBcastWait !== undefined) {
|
||||
DEBUG && debug("_checkStartBroadcastTimer(): Scheduling next check in " + nextBcastWait + "ms");
|
||||
this._broadcastTimer = setTimeout(() => this._checkStartBroadcastTimer(), nextBcastWait);
|
||||
}
|
||||
}
|
||||
|
||||
_query(name) {
|
||||
DEBUG && debug('query("' + name + '")');
|
||||
let packet = new DNSPacket();
|
||||
packet.setFlag('QR', DNS_QUERY_RESPONSE_CODES.QUERY);
|
||||
|
||||
// PTR Record
|
||||
packet.addRecord('QD', new DNSRecord({
|
||||
name: name,
|
||||
recordType: DNS_RECORD_TYPES.PTR,
|
||||
classCode: DNS_CLASS_CODES.IN,
|
||||
cacheFlush: true
|
||||
}));
|
||||
|
||||
let data = packet.serialize();
|
||||
|
||||
// Initialize the broadcast receiver socket in case it
|
||||
// hasn't already been started so we can listen for
|
||||
// multicast queries/announcements on all interfaces.
|
||||
this._getBroadcastReceiverSocket();
|
||||
|
||||
this._getQuerySocket().then((querySocket) => {
|
||||
DEBUG && debug('sending query on query socket ("' + name + '")');
|
||||
querySocket.send(MDNS_MULTICAST_GROUP, MDNS_PORT, data, data.length);
|
||||
});
|
||||
|
||||
// Automatically announce previously-discovered
|
||||
// services that match and haven't expired yet.
|
||||
setTimeout(() => {
|
||||
DEBUG && debug('announcing previously discovered services ("' + name + '")');
|
||||
let { serviceType } = _parseServiceDomainName(name);
|
||||
|
||||
this._clearExpiredDiscoveries();
|
||||
this._discovered.forEach((discovery, key) => {
|
||||
let serviceInfo = discovery.serviceInfo;
|
||||
if (serviceInfo.serviceType !== serviceType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let listeners = this._listeners.get(serviceInfo.serviceType) || [];
|
||||
listeners.forEach((listener) => {
|
||||
listener.onServiceFound(serviceInfo);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_clearExpiredDiscoveries() {
|
||||
this._discovered.forEach((discovery, key) => {
|
||||
if (discovery.expireTime < Date.now()) {
|
||||
this._discovered.delete(key);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_handleQueryPacket(packet, message) {
|
||||
packet.getRecords(['QD']).forEach((record) => {
|
||||
// Don't respond if the query's class code is not IN or ANY.
|
||||
if (record.classCode !== DNS_CLASS_CODES.IN &&
|
||||
record.classCode !== DNS_CLASS_CODES.ANY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't respond if the query's record type is not PTR or ANY.
|
||||
if (record.recordType !== DNS_RECORD_TYPES.PTR &&
|
||||
record.recordType !== DNS_RECORD_TYPES.ANY) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let [serviceKey, publishedService] of this._services) {
|
||||
DEBUG && debug("_handleQueryPacket: " + packet.toJSON());
|
||||
if (publishedService.ptrMatch(record.name)) {
|
||||
this._respondToQuery(serviceKey, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_makeServicePacket(service, addresses) {
|
||||
let packet = new DNSPacket();
|
||||
packet.setFlag('QR', DNS_QUERY_RESPONSE_CODES.RESPONSE);
|
||||
packet.setFlag('AA', DNS_AUTHORITATIVE_ANSWER_CODES.YES);
|
||||
|
||||
let host = service.host || _hostname;
|
||||
|
||||
// e.g.: foo-bar-service._http._tcp.local
|
||||
let serviceDomainName = service.serviceName + '.' + service.serviceType + '.local';
|
||||
|
||||
// PTR Record
|
||||
packet.addRecord('AN', new DNSResourceRecord({
|
||||
name: service.serviceType + '.local', // e.g.: _http._tcp.local
|
||||
recordType: DNS_RECORD_TYPES.PTR,
|
||||
data: serviceDomainName
|
||||
}));
|
||||
|
||||
// SRV Record
|
||||
packet.addRecord('AR', new DNSResourceRecord({
|
||||
name: serviceDomainName,
|
||||
recordType: DNS_RECORD_TYPES.SRV,
|
||||
classCode: DNS_CLASS_CODES.IN,
|
||||
cacheFlush: true,
|
||||
data: {
|
||||
priority: 0,
|
||||
weight: 0,
|
||||
port: service.port,
|
||||
target: host // e.g.: My-Android-Phone.local
|
||||
}
|
||||
}));
|
||||
|
||||
// A Records
|
||||
for (let address of addresses) {
|
||||
packet.addRecord('AR', new DNSResourceRecord({
|
||||
name: host,
|
||||
recordType: DNS_RECORD_TYPES.A,
|
||||
data: address
|
||||
}));
|
||||
}
|
||||
|
||||
// TXT Record
|
||||
packet.addRecord('AR', new DNSResourceRecord({
|
||||
name: serviceDomainName,
|
||||
recordType: DNS_RECORD_TYPES.TXT,
|
||||
classCode: DNS_CLASS_CODES.IN,
|
||||
cacheFlush: true,
|
||||
data: service.serviceAttrs || {}
|
||||
}));
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
_handleResponsePacket(packet, message) {
|
||||
let services = {};
|
||||
let hosts = {};
|
||||
|
||||
let srvRecords = packet.getRecords(['AN', 'AR'], DNS_RECORD_TYPES.SRV);
|
||||
let txtRecords = packet.getRecords(['AN', 'AR'], DNS_RECORD_TYPES.TXT);
|
||||
let ptrRecords = packet.getRecords(['AN', 'AR'], DNS_RECORD_TYPES.PTR);
|
||||
let aRecords = packet.getRecords(['AN', 'AR'], DNS_RECORD_TYPES.A);
|
||||
|
||||
srvRecords.forEach((record) => {
|
||||
let data = record.data || {};
|
||||
|
||||
services[record.name] = {
|
||||
host: data.target,
|
||||
port: data.port,
|
||||
ttl: record.ttl
|
||||
};
|
||||
});
|
||||
|
||||
txtRecords.forEach((record) => {
|
||||
if (!services[record.name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
services[record.name].attributes = record.data;
|
||||
});
|
||||
|
||||
aRecords.forEach((record) => {
|
||||
if (IsIpv4Address(record.data)) {
|
||||
hosts[record.name] = record.data;
|
||||
}
|
||||
});
|
||||
|
||||
ptrRecords.forEach((record) => {
|
||||
let name = record.data;
|
||||
if (!services[name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let {host, port} = services[name];
|
||||
if (!host || !port) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { serviceName, serviceType, domainName } = _parseServiceDomainName(name);
|
||||
if (!serviceName || !serviceType || !domainName) {
|
||||
return;
|
||||
}
|
||||
|
||||
let address = hosts[host];
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ttl = services[name].ttl || 0;
|
||||
let serviceInfo = {
|
||||
serviceName: serviceName,
|
||||
serviceType: serviceType,
|
||||
host: host,
|
||||
address: address,
|
||||
port: port,
|
||||
domainName: domainName,
|
||||
attributes: services[name].attributes || {}
|
||||
};
|
||||
|
||||
this._onServiceFound(serviceInfo, ttl);
|
||||
});
|
||||
}
|
||||
|
||||
_onServiceFound(serviceInfo, ttl = 0) {
|
||||
let expireTime = Date.now() + (ttl * 1000);
|
||||
let key = serviceInfo.serviceName + '.' +
|
||||
serviceInfo.serviceType + '.' +
|
||||
serviceInfo.domainName + ' @' +
|
||||
serviceInfo.address + ':' +
|
||||
serviceInfo.port;
|
||||
|
||||
// If this service was already discovered, just update
|
||||
// its expiration time and don't re-emit it.
|
||||
if (this._discovered.has(key)) {
|
||||
this._discovered.get(key).expireTime = expireTime;
|
||||
return;
|
||||
}
|
||||
|
||||
this._discovered.set(key, {
|
||||
serviceInfo: serviceInfo,
|
||||
expireTime: expireTime
|
||||
});
|
||||
|
||||
let listeners = this._listeners.get(serviceInfo.serviceType) || [];
|
||||
listeners.forEach((listener) => {
|
||||
listener.onServiceFound(serviceInfo);
|
||||
});
|
||||
|
||||
DEBUG && debug('_onServiceFound()' + serviceInfo.serviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-exclusive socket on 0.0.0.0:{random} to send
|
||||
* multicast queries on all interfaces. This socket does
|
||||
* not need to join a multicast group since it is still
|
||||
* able to *send* multicast queries, but it does not need
|
||||
* to *listen* for multicast queries/announcements since
|
||||
* the `_broadcastReceiverSocket` is already handling them.
|
||||
*/
|
||||
_getQuerySocket() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._querySocket) {
|
||||
this._querySocket = _openSocket('0.0.0.0', 0, {
|
||||
onPacketReceived: this._onPacketReceived.bind(this),
|
||||
onStopListening: this._onStopListening.bind(this)
|
||||
});
|
||||
}
|
||||
resolve(this._querySocket);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-exclusive socket on 0.0.0.0:5353 to listen
|
||||
* for multicast queries/announcements on all interfaces.
|
||||
* Since this socket needs to listen for multicast queries
|
||||
* and announcements, this socket joins the multicast
|
||||
* group on *all* interfaces (0.0.0.0).
|
||||
*/
|
||||
_getBroadcastReceiverSocket() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._broadcastReceiverSocket) {
|
||||
this._broadcastReceiverSocket = _openSocket('0.0.0.0', MDNS_PORT, {
|
||||
onPacketReceived: this._onPacketReceived.bind(this),
|
||||
onStopListening: this._onStopListening.bind(this)
|
||||
}, /* multicastInterface = */ '0.0.0.0');
|
||||
}
|
||||
resolve(this._broadcastReceiverSocket);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-exclusive socket for each interface on
|
||||
* {iface-ip}:5353 for sending query responses as
|
||||
* well as for listening for unicast queries. These
|
||||
* sockets do not need to join a multicast group
|
||||
* since they are still able to *send* multicast
|
||||
* query responses, but they do not need to *listen*
|
||||
* for multicast queries since the `_querySocket` is
|
||||
* already handling them.
|
||||
*/
|
||||
_getSockets() {
|
||||
return new Promise((resolve) => {
|
||||
if (this._sockets.size > 0) {
|
||||
resolve(this._sockets);
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.all([getAddresses(), getHostname()]).then(() => {
|
||||
_addresses.forEach((address) => {
|
||||
let socket = _openSocket(address, MDNS_PORT, null);
|
||||
this._sockets.set(address, socket);
|
||||
});
|
||||
|
||||
resolve(this._sockets);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_checkCloseSockets() {
|
||||
// Nothing to do if no sockets to close.
|
||||
if (this._sockets.size == 0)
|
||||
return;
|
||||
|
||||
// Don't close sockets if discovery listeners are still present.
|
||||
if (this._listeners.size > 0)
|
||||
return;
|
||||
|
||||
// Don't close sockets if advertised services are present.
|
||||
// Since we need to listen for service queries and respond to them.
|
||||
if (this._services.size > 0)
|
||||
return;
|
||||
|
||||
this._closeSockets();
|
||||
}
|
||||
|
||||
_closeSockets() {
|
||||
this._sockets.forEach(socket => socket.close());
|
||||
this._sockets.clear();
|
||||
}
|
||||
|
||||
_onPacketReceived(socket, message) {
|
||||
let packet = DNSPacket.parse(message.rawData);
|
||||
|
||||
switch (packet.getFlag('QR')) {
|
||||
case DNS_QUERY_RESPONSE_CODES.QUERY:
|
||||
this._handleQueryPacket(packet, message);
|
||||
break;
|
||||
case DNS_QUERY_RESPONSE_CODES.RESPONSE:
|
||||
this._handleResponsePacket(packet, message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onStopListening(socket, status) {
|
||||
DEBUG && debug('_onStopListening() ' + status);
|
||||
}
|
||||
|
||||
_addServiceListener(serviceType, listener) {
|
||||
let listeners = this._listeners.get(serviceType);
|
||||
if (!listeners) {
|
||||
listeners = [];
|
||||
this._listeners.set(serviceType, listeners);
|
||||
}
|
||||
|
||||
if (!listeners.find(l => l === listener)) {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
_removeServiceListener(serviceType, listener) {
|
||||
let listeners = this._listeners.get(serviceType);
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
let index = listeners.findIndex(l => l === listener);
|
||||
if (index >= 0) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
|
||||
if (listeners.length === 0) {
|
||||
this._listeners.delete(serviceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _addresses;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getAddresses() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (_addresses) {
|
||||
resolve(_addresses);
|
||||
return;
|
||||
}
|
||||
|
||||
networkInfoService.listNetworkAddresses({
|
||||
onListedNetworkAddresses(aAddressArray) {
|
||||
_addresses = aAddressArray.filter((address) => {
|
||||
return address.indexOf('%p2p') === -1 && // No WiFi Direct interfaces
|
||||
address.indexOf(':') === -1 && // XXX: No IPv6 for now
|
||||
address != "127.0.0.1" // No ipv4 loopback addresses.
|
||||
});
|
||||
|
||||
DEBUG && debug('getAddresses(): ' + _addresses);
|
||||
resolve(_addresses);
|
||||
},
|
||||
|
||||
onListNetworkAddressesFailed() {
|
||||
DEBUG && debug('getAddresses() FAILED!');
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let _hostname;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getHostname() {
|
||||
return new Promise((resolve) => {
|
||||
if (_hostname) {
|
||||
resolve(_hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
networkInfoService.getHostname({
|
||||
onGotHostname(aHostname) {
|
||||
_hostname = aHostname.replace(/\s/g, '-') + '.local';
|
||||
|
||||
DEBUG && debug('getHostname(): ' + _hostname);
|
||||
resolve(_hostname);
|
||||
},
|
||||
|
||||
onGetHostnameFailed() {
|
||||
DEBUG && debug('getHostname() FAILED');
|
||||
resolve('localhost');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse fully qualified domain name to service name, instance name,
|
||||
* and domain name. See https://tools.ietf.org/html/rfc6763#section-7.
|
||||
*
|
||||
* Example: 'foo-bar-service._http._tcp.local' -> {
|
||||
* serviceName: 'foo-bar-service',
|
||||
* serviceType: '_http._tcp',
|
||||
* domainName: 'local'
|
||||
* }
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function _parseServiceDomainName(serviceDomainName) {
|
||||
let parts = serviceDomainName.split('.');
|
||||
let index = Math.max(parts.lastIndexOf('_tcp'), parts.lastIndexOf('_udp'));
|
||||
|
||||
return {
|
||||
serviceName: parts.splice(0, index - 1).join('.'),
|
||||
serviceType: parts.splice(0, 2).join('.'),
|
||||
domainName: parts.join('.')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _propertyBagToObject(propBag) {
|
||||
let result = {};
|
||||
if (propBag.QueryInterface) {
|
||||
propBag.QueryInterface(Ci.nsIPropertyBag2);
|
||||
let propEnum = propBag.enumerator;
|
||||
while (propEnum.hasMoreElements()) {
|
||||
let prop = propEnum.getNext().QueryInterface(Ci.nsIProperty);
|
||||
result[prop.name] = prop.value.toString();
|
||||
}
|
||||
} else {
|
||||
for (let name in propBag) {
|
||||
result[name] = propBag[name].toString();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _openSocket(addr, port, handler, multicastInterface) {
|
||||
let socket = Cc['@mozilla.org/network/udp-socket;1'].createInstance(Ci.nsIUDPSocket);
|
||||
socket.init2(addr, port, Services.scriptSecurityManager.getSystemPrincipal(), true);
|
||||
|
||||
if (handler) {
|
||||
socket.asyncListen({
|
||||
onPacketReceived: handler.onPacketReceived,
|
||||
onStopListening: handler.onStopListening
|
||||
});
|
||||
}
|
||||
|
||||
if (multicastInterface) {
|
||||
socket.joinMulticast(MDNS_MULTICAST_GROUP, multicastInterface);
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
56
netwerk/dns/mdns/libmdns/moz.build
Normal file
56
netwerk/dns/mdns/libmdns/moz.build
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' or \
|
||||
(CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] >= '16'):
|
||||
UNIFIED_SOURCES += [
|
||||
'MDNSResponderOperator.cpp',
|
||||
'MDNSResponderReply.cpp',
|
||||
'nsDNSServiceDiscovery.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'external/mdnsresponder/mDNSShared',
|
||||
]
|
||||
]
|
||||
|
||||
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',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
EXTRA_JS_MODULES += [
|
||||
'MulticastDNSAndroid.jsm',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsDNSServiceInfo.cpp',
|
||||
'nsMulticastDNSModule.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
285
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
Normal file
285
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/* -*- 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"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <cutils/properties.h>
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void
|
||||
StartService()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
char value[PROPERTY_VALUE_MAX] = { '\0' };
|
||||
property_get("init.svc.mdnsd", value, "");
|
||||
|
||||
if (strcmp(value, "running") == 0) {
|
||||
return;
|
||||
}
|
||||
property_set("ctl.start", "mdnsd");
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
}
|
||||
|
||||
inline void
|
||||
StopService()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
char value[PROPERTY_VALUE_MAX] = { '\0' };
|
||||
property_get("init.svc.mdnsd", value, "");
|
||||
|
||||
if (strcmp(value, "stopped") == 0) {
|
||||
return;
|
||||
}
|
||||
property_set("ctl.stop", "mdnsd");
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
StopService();
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
48
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
Normal file
48
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- 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 "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
|
||||
201
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.js
Normal file
201
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.js
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* 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/. */
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let { PlatformInfo } = ExtensionUtils;
|
||||
|
||||
if (PlatformInfo.os == "android" && !Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
|
||||
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
|
||||
} else {
|
||||
Cu.import("resource://gre/modules/MulticastDNS.jsm");
|
||||
}
|
||||
|
||||
const DNSSERVICEDISCOVERY_CID = Components.ID("{f9346d98-f27a-4e89-b744-493843416480}");
|
||||
const DNSSERVICEDISCOVERY_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1";
|
||||
const DNSSERVICEINFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1";
|
||||
|
||||
function log(aMsg) {
|
||||
dump("-*- nsDNSServiceDiscovery.js : " + aMsg + "\n");
|
||||
}
|
||||
|
||||
function generateUuid() {
|
||||
var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator);
|
||||
return uuidGenerator.generateUUID().toString();
|
||||
}
|
||||
|
||||
// Helper class to transform return objects to correct type.
|
||||
function ListenerWrapper(aListener, aMdns) {
|
||||
this.listener = aListener;
|
||||
this.mdns = aMdns;
|
||||
|
||||
this.discoveryStarting = false;
|
||||
this.stopDiscovery = false;
|
||||
|
||||
this.registrationStarting = false;
|
||||
this.stopRegistration = false;
|
||||
|
||||
this.uuid = generateUuid();
|
||||
}
|
||||
|
||||
ListenerWrapper.prototype = {
|
||||
// Helper function for transforming an Object into nsIDNSServiceInfo.
|
||||
makeServiceInfo: function (aServiceInfo) {
|
||||
let serviceInfo = Cc[DNSSERVICEINFO_CONTRACT_ID].createInstance(Ci.nsIDNSServiceInfo);
|
||||
|
||||
for (let name of ['host', 'address', 'port', 'serviceName', 'serviceType']) {
|
||||
try {
|
||||
serviceInfo[name] = aServiceInfo[name];
|
||||
} catch (e) {
|
||||
// ignore exceptions
|
||||
}
|
||||
}
|
||||
|
||||
let attributes;
|
||||
try {
|
||||
attributes = _toPropertyBag2(aServiceInfo.attributes);
|
||||
} catch (err) {
|
||||
// Ignore unset attributes in object.
|
||||
log("Caught unset attributes error: " + err + " - " + err.stack);
|
||||
attributes = Cc['@mozilla.org/hash-property-bag;1']
|
||||
.createInstance(Ci.nsIWritablePropertyBag2);
|
||||
}
|
||||
serviceInfo.attributes = attributes;
|
||||
|
||||
return serviceInfo;
|
||||
},
|
||||
|
||||
/* transparent types */
|
||||
onDiscoveryStarted: function(aServiceType) {
|
||||
this.discoveryStarting = false;
|
||||
this.listener.onDiscoveryStarted(aServiceType);
|
||||
|
||||
if (this.stopDiscovery) {
|
||||
this.mdns.stopDiscovery(aServiceType, this);
|
||||
}
|
||||
},
|
||||
onDiscoveryStopped: function(aServiceType) {
|
||||
this.listener.onDiscoveryStopped(aServiceType);
|
||||
},
|
||||
onStartDiscoveryFailed: function(aServiceType, aErrorCode) {
|
||||
log('onStartDiscoveryFailed: ' + aServiceType + ' (' + aErrorCode + ')');
|
||||
this.discoveryStarting = false;
|
||||
this.stopDiscovery = true;
|
||||
this.listener.onStartDiscoveryFailed(aServiceType, aErrorCode);
|
||||
},
|
||||
onStopDiscoveryFailed: function(aServiceType, aErrorCode) {
|
||||
log('onStopDiscoveryFailed: ' + aServiceType + ' (' + aErrorCode + ')');
|
||||
this.listener.onStopDiscoveryFailed(aServiceType, aErrorCode);
|
||||
},
|
||||
|
||||
/* transform types */
|
||||
onServiceFound: function(aServiceInfo) {
|
||||
this.listener.onServiceFound(this.makeServiceInfo(aServiceInfo));
|
||||
},
|
||||
onServiceLost: function(aServiceInfo) {
|
||||
this.listener.onServiceLost(this.makeServiceInfo(aServiceInfo));
|
||||
},
|
||||
onServiceRegistered: function(aServiceInfo) {
|
||||
this.registrationStarting = false;
|
||||
this.listener.onServiceRegistered(this.makeServiceInfo(aServiceInfo));
|
||||
|
||||
if (this.stopRegistration) {
|
||||
this.mdns.unregisterService(aServiceInfo, this);
|
||||
}
|
||||
},
|
||||
onServiceUnregistered: function(aServiceInfo) {
|
||||
this.listener.onServiceUnregistered(this.makeServiceInfo(aServiceInfo));
|
||||
},
|
||||
onServiceResolved: function(aServiceInfo) {
|
||||
this.listener.onServiceResolved(this.makeServiceInfo(aServiceInfo));
|
||||
},
|
||||
|
||||
onRegistrationFailed: function(aServiceInfo, aErrorCode) {
|
||||
log('onRegistrationFailed: (' + aErrorCode + ')');
|
||||
this.registrationStarting = false;
|
||||
this.stopRegistration = true;
|
||||
this.listener.onRegistrationFailed(this.makeServiceInfo(aServiceInfo), aErrorCode);
|
||||
},
|
||||
onUnregistrationFailed: function(aServiceInfo, aErrorCode) {
|
||||
log('onUnregistrationFailed: (' + aErrorCode + ')');
|
||||
this.listener.onUnregistrationFailed(this.makeServiceInfo(aServiceInfo), aErrorCode);
|
||||
},
|
||||
onResolveFailed: function(aServiceInfo, aErrorCode) {
|
||||
log('onResolveFailed: (' + aErrorCode + ')');
|
||||
this.listener.onResolveFailed(this.makeServiceInfo(aServiceInfo), aErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
function nsDNSServiceDiscovery() {
|
||||
log("nsDNSServiceDiscovery");
|
||||
this.mdns = new MulticastDNS();
|
||||
}
|
||||
|
||||
nsDNSServiceDiscovery.prototype = {
|
||||
classID: DNSSERVICEDISCOVERY_CID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIDNSServiceDiscovery]),
|
||||
|
||||
startDiscovery: function(aServiceType, aListener) {
|
||||
log("startDiscovery");
|
||||
let listener = new ListenerWrapper(aListener, this.mdns);
|
||||
listener.discoveryStarting = true;
|
||||
this.mdns.startDiscovery(aServiceType, listener);
|
||||
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
|
||||
cancel: (function() {
|
||||
if (this.discoveryStarting || this.stopDiscovery) {
|
||||
this.stopDiscovery = true;
|
||||
return;
|
||||
}
|
||||
this.mdns.stopDiscovery(aServiceType, listener);
|
||||
}).bind(listener)
|
||||
};
|
||||
},
|
||||
|
||||
registerService: function(aServiceInfo, aListener) {
|
||||
log("registerService");
|
||||
let listener = new ListenerWrapper(aListener, this.mdns);
|
||||
listener.registrationStarting = true;
|
||||
this.mdns.registerService(aServiceInfo, listener);
|
||||
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
|
||||
cancel: (function() {
|
||||
if (this.registrationStarting || this.stopRegistration) {
|
||||
this.stopRegistration = true;
|
||||
return;
|
||||
}
|
||||
this.mdns.unregisterService(aServiceInfo, listener);
|
||||
}).bind(listener)
|
||||
};
|
||||
},
|
||||
|
||||
resolveService: function(aServiceInfo, aListener) {
|
||||
log("resolveService");
|
||||
this.mdns.resolveService(aServiceInfo, new ListenerWrapper(aListener));
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDNSServiceDiscovery]);
|
||||
|
||||
function _toPropertyBag2(obj)
|
||||
{
|
||||
if (obj.QueryInterface) {
|
||||
return obj.QueryInterface(Ci.nsIPropertyBag2);
|
||||
}
|
||||
|
||||
let result = Cc['@mozilla.org/hash-property-bag;1']
|
||||
.createInstance(Ci.nsIWritablePropertyBag2);
|
||||
for (let name in obj) {
|
||||
result.setPropertyAsAString(name, obj[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
3
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.manifest
Normal file
3
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.manifest
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# nsDNSServiceDiscovery.js
|
||||
component {f9346d98-f27a-4e89-b744-493843416480} nsDNSServiceDiscovery.js
|
||||
contract @mozilla.org/toolkit/components/mdnsresponder/dns-sd;1 {f9346d98-f27a-4e89-b744-493843416480}
|
||||
209
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
Normal file
209
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/* -*- 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 "nsDNSServiceInfo.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsIProperty.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDNSServiceInfo, nsIDNSServiceInfo)
|
||||
|
||||
nsDNSServiceInfo::nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo)
|
||||
{
|
||||
if (NS_WARN_IF(!aServiceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString str;
|
||||
uint16_t value;
|
||||
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetHost(str))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetHost(str)));
|
||||
}
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetAddress(str))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetAddress(str)));
|
||||
}
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetPort(&value))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetPort(value)));
|
||||
}
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetServiceName(str))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetServiceName(str)));
|
||||
}
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetServiceType(str))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetServiceType(str)));
|
||||
}
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetDomainName(str))) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetDomainName(str)));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> attributes; // deep copy
|
||||
if (NS_SUCCEEDED(aServiceInfo->GetAttributes(getter_AddRefs(attributes)))) {
|
||||
nsCOMPtr<nsISimpleEnumerator> enumerator;
|
||||
if (NS_WARN_IF(NS_FAILED(attributes->GetEnumerator(getter_AddRefs(enumerator))))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWritablePropertyBag2> newAttributes = new nsHashPropertyBag();
|
||||
|
||||
bool hasMoreElements;
|
||||
while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) &&
|
||||
hasMoreElements) {
|
||||
nsCOMPtr<nsISupports> element;
|
||||
Unused <<
|
||||
NS_WARN_IF(NS_FAILED(enumerator->GetNext(getter_AddRefs(element))));
|
||||
nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
|
||||
MOZ_ASSERT(property);
|
||||
|
||||
nsAutoString name;
|
||||
nsCOMPtr<nsIVariant> value;
|
||||
Unused << NS_WARN_IF(NS_FAILED(property->GetName(name)));
|
||||
Unused << NS_WARN_IF(NS_FAILED(property->GetValue(getter_AddRefs(value))));
|
||||
nsAutoCString valueStr;
|
||||
Unused << NS_WARN_IF(NS_FAILED(value->GetAsACString(valueStr)));
|
||||
|
||||
Unused << NS_WARN_IF(NS_FAILED(newAttributes->SetPropertyAsACString(name, valueStr)));
|
||||
}
|
||||
|
||||
Unused << NS_WARN_IF(NS_FAILED(SetAttributes(newAttributes)));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetHost(nsACString& aHost)
|
||||
{
|
||||
if (!mIsHostSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
aHost = mHost;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetHost(const nsACString& aHost)
|
||||
{
|
||||
mHost = aHost;
|
||||
mIsHostSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetAddress(nsACString& aAddress)
|
||||
{
|
||||
if (!mIsAddressSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
aAddress = mAddress;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetAddress(const nsACString& aAddress)
|
||||
{
|
||||
mAddress = aAddress;
|
||||
mIsAddressSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetPort(uint16_t* aPort)
|
||||
{
|
||||
if (NS_WARN_IF(!aPort)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (!mIsPortSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
*aPort = mPort;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetPort(uint16_t aPort)
|
||||
{
|
||||
mPort = aPort;
|
||||
mIsPortSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetServiceName(nsACString& aServiceName)
|
||||
{
|
||||
if (!mIsServiceNameSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
aServiceName = mServiceName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetServiceName(const nsACString& aServiceName)
|
||||
{
|
||||
mServiceName = aServiceName;
|
||||
mIsServiceNameSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetServiceType(nsACString& aServiceType)
|
||||
{
|
||||
if (!mIsServiceTypeSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
aServiceType = mServiceType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetServiceType(const nsACString& aServiceType)
|
||||
{
|
||||
mServiceType = aServiceType;
|
||||
mIsServiceTypeSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetDomainName(nsACString& aDomainName)
|
||||
{
|
||||
if (!mIsDomainNameSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
aDomainName = mDomainName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetDomainName(const nsACString& aDomainName)
|
||||
{
|
||||
mDomainName = aDomainName;
|
||||
mIsDomainNameSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::GetAttributes(nsIPropertyBag2** aAttributes)
|
||||
{
|
||||
if (!mIsAttributesSet) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
nsCOMPtr<nsIPropertyBag2> attributes(mAttributes);
|
||||
attributes.forget(aAttributes);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSServiceInfo::SetAttributes(nsIPropertyBag2* aAttributes)
|
||||
{
|
||||
mAttributes = aAttributes;
|
||||
mIsAttributesSet = aAttributes ? true : false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
50
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
Normal file
50
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- 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_nsDNSServiceInfo_h
|
||||
#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDNSServiceDiscovery.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsDNSServiceInfo final : public nsIDNSServiceInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDNSSERVICEINFO
|
||||
|
||||
explicit nsDNSServiceInfo() = default;
|
||||
explicit nsDNSServiceInfo(nsIDNSServiceInfo* aServiceInfo);
|
||||
|
||||
private:
|
||||
virtual ~nsDNSServiceInfo() = default;
|
||||
|
||||
private:
|
||||
nsCString mHost;
|
||||
nsCString mAddress;
|
||||
uint16_t mPort = 0;
|
||||
nsCString mServiceName;
|
||||
nsCString mServiceType;
|
||||
nsCString mDomainName;
|
||||
nsCOMPtr<nsIPropertyBag2> mAttributes;
|
||||
|
||||
bool mIsHostSet = false;
|
||||
bool mIsAddressSet = false;
|
||||
bool mIsPortSet = false;
|
||||
bool mIsServiceNameSet = false;
|
||||
bool mIsServiceTypeSet = false;
|
||||
bool mIsDomainNameSet = false;
|
||||
bool mIsAttributesSet = false;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceInfo_h
|
||||
61
netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
Normal file
61
netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#if defined(MOZ_WIDGET_COCOA) || (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 16)
|
||||
#define ENABLE_DNS_SERVICE_DISCOVERY
|
||||
#endif
|
||||
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
|
||||
#ifdef ENABLE_DNS_SERVICE_DISCOVERY
|
||||
#include "nsDNSServiceDiscovery.h"
|
||||
#endif
|
||||
|
||||
#include "nsDNSServiceInfo.h"
|
||||
|
||||
#ifdef ENABLE_DNS_SERVICE_DISCOVERY
|
||||
using mozilla::net::nsDNSServiceDiscovery;
|
||||
#define DNSSERVICEDISCOVERY_CID \
|
||||
{0x8df43d23, 0xd3f9, 0x4dd5, \
|
||||
{ 0xb9, 0x65, 0xde, 0x2c, 0xa3, 0xf6, 0xa4, 0x2c }}
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDNSServiceDiscovery, Init)
|
||||
NS_DEFINE_NAMED_CID(DNSSERVICEDISCOVERY_CID);
|
||||
#endif // ENABLE_DNS_SERVICE_DISCOVERY
|
||||
|
||||
using mozilla::net::nsDNSServiceInfo;
|
||||
#define DNSSERVICEINFO_CID \
|
||||
{0x14a50f2b, 0x7ff6, 0x48a5, \
|
||||
{ 0x88, 0xe3, 0x61, 0x5f, 0xd1, 0x11, 0xf5, 0xd3 }}
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDNSServiceInfo)
|
||||
NS_DEFINE_NAMED_CID(DNSSERVICEINFO_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry knsDNSServiceDiscoveryCIDs[] = {
|
||||
#ifdef ENABLE_DNS_SERVICE_DISCOVERY
|
||||
{ &kDNSSERVICEDISCOVERY_CID, false, nullptr, nsDNSServiceDiscoveryConstructor },
|
||||
#endif
|
||||
{ &kDNSSERVICEINFO_CID, false, nullptr, nsDNSServiceInfoConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry knsDNSServiceDiscoveryContracts[] = {
|
||||
#ifdef ENABLE_DNS_SERVICE_DISCOVERY
|
||||
{ DNSSERVICEDISCOVERY_CONTRACT_ID, &kDNSSERVICEDISCOVERY_CID },
|
||||
#endif
|
||||
{ DNSSERVICEINFO_CONTRACT_ID, &kDNSSERVICEINFO_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::CategoryEntry knsDNSServiceDiscoveryCategories[] = {
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module knsDNSServiceDiscoveryModule = {
|
||||
mozilla::Module::kVersion,
|
||||
knsDNSServiceDiscoveryCIDs,
|
||||
knsDNSServiceDiscoveryContracts,
|
||||
knsDNSServiceDiscoveryCategories
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(nsDNSServiceDiscoveryModule) = &knsDNSServiceDiscoveryModule;
|
||||
Loading…
Add table
Add a link
Reference in a new issue