зеркало из https://github.com/mozilla/gecko-dev.git
1318 строки
37 KiB
C++
1318 строки
37 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "mozilla/dom/FlyWebService.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/FlyWebPublishedServerIPC.h"
|
|
#include "mozilla/AddonPathService.h"
|
|
#include "nsISocketTransportService.h"
|
|
#include "mdns/libmdns/nsDNSServiceInfo.h"
|
|
#include "nsIUUIDGenerator.h"
|
|
#include "nsStandardURL.h"
|
|
#include "mozilla/Services.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
|
|
#include "prnetdb.h"
|
|
#include "DNS.h"
|
|
#include "nsContentPermissionHelper.h"
|
|
#include "nsSocketTransportService2.h"
|
|
#include "nsSocketTransport2.h"
|
|
#include "nsHashPropertyBag.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIProperty.h"
|
|
#include "nsICertOverrideService.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
struct FlyWebPublishOptions;
|
|
|
|
static LazyLogModule gFlyWebServiceLog("FlyWebService");
|
|
#undef LOG_I
|
|
#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
|
|
#undef LOG_E
|
|
#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__))
|
|
|
|
#undef LOG_TEST_I
|
|
#define LOG_TEST_I(...) MOZ_LOG_TEST(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug)
|
|
|
|
class FlyWebPublishServerPermissionCheck final
|
|
: public nsIContentPermissionRequest
|
|
, public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
FlyWebPublishServerPermissionCheck(const nsCString& aServiceName, uint64_t aWindowID,
|
|
FlyWebPublishedServer* aServer)
|
|
: mServiceName(aServiceName)
|
|
, mWindowID(aWindowID)
|
|
, mServer(aServer)
|
|
{}
|
|
|
|
uint64_t WindowID() const
|
|
{
|
|
return mWindowID;
|
|
}
|
|
|
|
NS_IMETHOD Run() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
|
if (!globalWindow) {
|
|
return Cancel();
|
|
}
|
|
mWindow = globalWindow->AsInner();
|
|
if (NS_WARN_IF(!mWindow)) {
|
|
return Cancel();
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
|
|
if (NS_WARN_IF(!doc)) {
|
|
return Cancel();
|
|
}
|
|
|
|
mPrincipal = doc->NodePrincipal();
|
|
MOZ_ASSERT(mPrincipal);
|
|
|
|
mRequester = new nsContentPermissionRequester(mWindow);
|
|
return nsContentPermissionUtils::AskPermission(this, mWindow);
|
|
}
|
|
|
|
NS_IMETHOD Cancel() override
|
|
{
|
|
Resolve(false);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD Allow(JS::HandleValue aChoices) override
|
|
{
|
|
MOZ_ASSERT(aChoices.isUndefined());
|
|
Resolve(true);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetTypes(nsIArray** aTypes) override
|
|
{
|
|
nsTArray<nsString> emptyOptions;
|
|
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("flyweb-publish-server"),
|
|
NS_LITERAL_CSTRING("unused"), emptyOptions, aTypes);
|
|
}
|
|
|
|
NS_IMETHOD GetRequester(nsIContentPermissionRequester** aRequester) override
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aRequester);
|
|
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
|
requester.forget(aRequester);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetPrincipal(nsIPrincipal** aRequestingPrincipal) override
|
|
{
|
|
NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetWindow(mozIDOMWindow** aRequestingWindow) override
|
|
{
|
|
NS_IF_ADDREF(*aRequestingWindow = mWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetElement(nsIDOMElement** aRequestingElement) override
|
|
{
|
|
*aRequestingElement = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
void Resolve(bool aResolve)
|
|
{
|
|
mServer->PermissionGranted(aResolve);
|
|
}
|
|
|
|
virtual ~FlyWebPublishServerPermissionCheck() = default;
|
|
|
|
nsCString mServiceName;
|
|
uint64_t mWindowID;
|
|
RefPtr<FlyWebPublishedServer> mServer;
|
|
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
|
nsCOMPtr<nsIPrincipal> mPrincipal;
|
|
nsCOMPtr<nsIContentPermissionRequester> mRequester;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(FlyWebPublishServerPermissionCheck,
|
|
nsIContentPermissionRequest,
|
|
nsIRunnable)
|
|
|
|
class FlyWebMDNSService final
|
|
: public nsIDNSServiceDiscoveryListener
|
|
, public nsIDNSServiceResolveListener
|
|
, public nsIDNSRegistrationListener
|
|
, public nsITimerCallback
|
|
{
|
|
friend class FlyWebService;
|
|
|
|
private:
|
|
enum DiscoveryState {
|
|
DISCOVERY_IDLE,
|
|
DISCOVERY_STARTING,
|
|
DISCOVERY_RUNNING,
|
|
DISCOVERY_STOPPING
|
|
};
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
|
|
NS_DECL_NSIDNSSERVICERESOLVELISTENER
|
|
NS_DECL_NSIDNSREGISTRATIONLISTENER
|
|
NS_DECL_NSITIMERCALLBACK
|
|
|
|
explicit FlyWebMDNSService(FlyWebService* aService,
|
|
const nsACString& aServiceType);
|
|
|
|
private:
|
|
virtual ~FlyWebMDNSService() = default;
|
|
|
|
nsresult Init();
|
|
nsresult StartDiscovery();
|
|
nsresult StopDiscovery();
|
|
|
|
void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices);
|
|
bool HasService(const nsAString& aServiceId);
|
|
nsresult PairWithService(const nsAString& aServiceId,
|
|
UniquePtr<FlyWebService::PairedInfo>& aInfo);
|
|
|
|
nsresult StartDiscoveryOf(FlyWebPublishedServerImpl* aServer);
|
|
|
|
void EnsureDiscoveryStarted();
|
|
void EnsureDiscoveryStopped();
|
|
|
|
// Cycle-breaking link to manager.
|
|
FlyWebService* mService;
|
|
nsCString mServiceType;
|
|
|
|
// Indicates the desired state of the system. If mDiscoveryActive is true,
|
|
// it indicates that backend discovery "should be happening", and discovery
|
|
// events should be forwarded to listeners.
|
|
// If false, the backend discovery "should be idle", and any discovery events
|
|
// that show up should not be forwarded to listeners.
|
|
bool mDiscoveryActive;
|
|
|
|
uint32_t mNumConsecutiveStartDiscoveryFailures;
|
|
|
|
// Represents the internal discovery state as it relates to nsDNSServiceDiscovery.
|
|
// When mDiscoveryActive is true, this state will periodically loop from
|
|
// (IDLE => STARTING => RUNNING => STOPPING => IDLE).
|
|
DiscoveryState mDiscoveryState;
|
|
|
|
nsCOMPtr<nsITimer> mDiscoveryStartTimer;
|
|
nsCOMPtr<nsITimer> mDiscoveryStopTimer;
|
|
nsCOMPtr<nsIDNSServiceDiscovery> mDNSServiceDiscovery;
|
|
nsCOMPtr<nsICancelable> mCancelDiscovery;
|
|
nsTHashtable<nsStringHashKey> mNewServiceSet;
|
|
|
|
struct DiscoveredInfo
|
|
{
|
|
explicit DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo);
|
|
FlyWebDiscoveredService mService;
|
|
nsCOMPtr<nsIDNSServiceInfo> mDNSServiceInfo;
|
|
};
|
|
nsClassHashtable<nsStringHashKey, DiscoveredInfo> mServiceMap;
|
|
};
|
|
|
|
void
|
|
LogDNSInfo(nsIDNSServiceInfo* aServiceInfo, const char* aFunc)
|
|
{
|
|
if (!LOG_TEST_I()) {
|
|
return;
|
|
}
|
|
|
|
nsCString tmp;
|
|
aServiceInfo->GetServiceName(tmp);
|
|
LOG_I("%s: serviceName=%s", aFunc, tmp.get());
|
|
|
|
aServiceInfo->GetHost(tmp);
|
|
LOG_I("%s: host=%s", aFunc, tmp.get());
|
|
|
|
aServiceInfo->GetAddress(tmp);
|
|
LOG_I("%s: address=%s", aFunc, tmp.get());
|
|
|
|
uint16_t port = -2;
|
|
aServiceInfo->GetPort(&port);
|
|
LOG_I("%s: port=%d", aFunc, (int)port);
|
|
|
|
nsCOMPtr<nsIPropertyBag2> attributes;
|
|
aServiceInfo->GetAttributes(getter_AddRefs(attributes));
|
|
if (!attributes) {
|
|
LOG_I("%s: no attributes", aFunc);
|
|
} else {
|
|
nsCOMPtr<nsISimpleEnumerator> enumerator;
|
|
attributes->GetEnumerator(getter_AddRefs(enumerator));
|
|
MOZ_ASSERT(enumerator);
|
|
|
|
LOG_I("%s: attributes start", aFunc);
|
|
|
|
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;
|
|
nsresult rv = value->GetAsACString(str);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
LOG_I("%s: attribute name=%s value=%s", aFunc,
|
|
NS_ConvertUTF16toUTF8(name).get(), str.get());
|
|
} else {
|
|
uint16_t type;
|
|
MOZ_ALWAYS_SUCCEEDS(value->GetDataType(&type));
|
|
LOG_I("%s: attribute *unstringifiable* name=%s type=%d", aFunc,
|
|
NS_ConvertUTF16toUTF8(name).get(), (int)type);
|
|
}
|
|
}
|
|
|
|
LOG_I("%s: attributes end", aFunc);
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(FlyWebMDNSService,
|
|
nsIDNSServiceDiscoveryListener,
|
|
nsIDNSServiceResolveListener,
|
|
nsIDNSRegistrationListener,
|
|
nsITimerCallback)
|
|
|
|
FlyWebMDNSService::FlyWebMDNSService(
|
|
FlyWebService* aService,
|
|
const nsACString& aServiceType)
|
|
: mService(aService)
|
|
, mServiceType(aServiceType)
|
|
, mDiscoveryActive(false)
|
|
, mNumConsecutiveStartDiscoveryFailures(0)
|
|
, mDiscoveryState(DISCOVERY_IDLE)
|
|
{}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnDiscoveryStarted(const nsACString& aServiceType)
|
|
{
|
|
MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING);
|
|
mDiscoveryState = DISCOVERY_RUNNING;
|
|
// Reset consecutive start discovery failures.
|
|
mNumConsecutiveStartDiscoveryFailures = 0;
|
|
LOG_I("===========================================");
|
|
LOG_I("MDNSService::OnDiscoveryStarted(%s)", PromiseFlatCString(aServiceType).get());
|
|
LOG_I("===========================================");
|
|
|
|
// Clear the new service array.
|
|
mNewServiceSet.Clear();
|
|
|
|
// If service discovery is inactive, then stop network discovery immediately.
|
|
if (!mDiscoveryActive) {
|
|
// Set the stop timer to fire immediately.
|
|
Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
|
|
return NS_OK;
|
|
}
|
|
|
|
// Otherwise, set the stop timer to fire in 5 seconds.
|
|
Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 5 * 1000, nsITimer::TYPE_ONE_SHOT)));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnDiscoveryStopped(const nsACString& aServiceType)
|
|
{
|
|
LOG_I("///////////////////////////////////////////");
|
|
LOG_I("MDNSService::OnDiscoveryStopped(%s)", PromiseFlatCString(aServiceType).get());
|
|
LOG_I("///////////////////////////////////////////");
|
|
MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING);
|
|
mDiscoveryState = DISCOVERY_IDLE;
|
|
|
|
// If service discovery is inactive, then discard all results and do not proceed.
|
|
if (!mDiscoveryActive) {
|
|
mServiceMap.Clear();
|
|
mNewServiceSet.Clear();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Process the service map, add to the pair map.
|
|
for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) {
|
|
DiscoveredInfo* service = iter.UserData();
|
|
|
|
if (!mNewServiceSet.Contains(service->mService.mServiceId)) {
|
|
iter.Remove();
|
|
}
|
|
}
|
|
|
|
// Notify FlyWebService of changed service list.
|
|
mService->NotifyDiscoveredServicesChanged();
|
|
|
|
// Start discovery again immediately.
|
|
Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceFound");
|
|
|
|
// If discovery is not active, don't do anything with the result.
|
|
// If there is no discovery underway, ignore this.
|
|
if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Discovery is underway - resolve the service.
|
|
nsresult rv = mDNSServiceDiscovery->ResolveService(aServiceInfo, this);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnServiceLost(nsIDNSServiceInfo* aServiceInfo)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceLost");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnStartDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode)
|
|
{
|
|
LOG_E("MDNSService::OnStartDiscoveryFailed(%s): %d", PromiseFlatCString(aServiceType).get(), (int) aErrorCode);
|
|
|
|
MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING);
|
|
mDiscoveryState = DISCOVERY_IDLE;
|
|
mNumConsecutiveStartDiscoveryFailures++;
|
|
|
|
// If discovery is active, and the number of consecutive failures is < 3, try starting again.
|
|
if (mDiscoveryActive && mNumConsecutiveStartDiscoveryFailures < 3) {
|
|
Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnStopDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode)
|
|
{
|
|
LOG_E("MDNSService::OnStopDiscoveryFailed(%s)", PromiseFlatCString(aServiceType).get());
|
|
MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING);
|
|
mDiscoveryState = DISCOVERY_IDLE;
|
|
|
|
// If discovery is active, start discovery again immediately.
|
|
if (mDiscoveryActive) {
|
|
Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static bool
|
|
IsAcceptableServiceAddress(const nsCString& addr)
|
|
{
|
|
PRNetAddr prNetAddr;
|
|
PRStatus status = PR_StringToNetAddr(addr.get(), &prNetAddr);
|
|
if (status == PR_FAILURE) {
|
|
return false;
|
|
}
|
|
// Only allow ipv4 addreses for now.
|
|
return prNetAddr.raw.family == PR_AF_INET;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceResolved");
|
|
|
|
// If discovery is not active, don't do anything with the result.
|
|
// If there is no discovery underway, ignore this resolve.
|
|
if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
nsCString address;
|
|
rv = aServiceInfo->GetAddress(address);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (!IsAcceptableServiceAddress(address)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Create a new serviceInfo and stuff it in the new service array.
|
|
UniquePtr<DiscoveredInfo> svc(new DiscoveredInfo(aServiceInfo));
|
|
mNewServiceSet.PutEntry(svc->mService.mServiceId);
|
|
|
|
DiscoveredInfo* existingSvc =
|
|
mServiceMap.Get(svc->mService.mServiceId);
|
|
if (existingSvc) {
|
|
// Update the underlying DNS service info, but leave the old object in place.
|
|
existingSvc->mDNSServiceInfo = aServiceInfo;
|
|
} else {
|
|
DiscoveredInfo* info = svc.release();
|
|
mServiceMap.Put(info->mService.mServiceId, info);
|
|
}
|
|
|
|
// Notify FlyWebService of changed service list.
|
|
mService->NotifyDiscoveredServicesChanged();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
FlyWebMDNSService::DiscoveredInfo::DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo)
|
|
: mDNSServiceInfo(aDNSServiceInfo)
|
|
{
|
|
nsCString tmp;
|
|
DebugOnly<nsresult> drv = aDNSServiceInfo->GetServiceName(tmp);
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
CopyUTF8toUTF16(tmp, mService.mDisplayName);
|
|
|
|
mService.mTransport = NS_LITERAL_STRING("mdns");
|
|
|
|
drv = aDNSServiceInfo->GetServiceType(tmp);
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
CopyUTF8toUTF16(tmp, mService.mServiceType);
|
|
|
|
nsCOMPtr<nsIPropertyBag2> attrs;
|
|
drv = aDNSServiceInfo->GetAttributes(getter_AddRefs(attrs));
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
if (attrs) {
|
|
attrs->GetPropertyAsAString(NS_LITERAL_STRING("cert"), mService.mCert);
|
|
attrs->GetPropertyAsAString(NS_LITERAL_STRING("path"), mService.mPath);
|
|
}
|
|
|
|
// Construct a service id from the name, host, address, and port.
|
|
nsCString cHost;
|
|
drv = aDNSServiceInfo->GetHost(cHost);
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
|
|
nsCString cAddress;
|
|
drv = aDNSServiceInfo->GetAddress(cAddress);
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
|
|
uint16_t port;
|
|
drv = aDNSServiceInfo->GetPort(&port);
|
|
MOZ_ASSERT(NS_SUCCEEDED(drv));
|
|
nsAutoString portStr;
|
|
portStr.AppendInt(port, 10);
|
|
|
|
mService.mServiceId =
|
|
NS_ConvertUTF8toUTF16(cAddress) +
|
|
NS_LITERAL_STRING(":") +
|
|
portStr +
|
|
NS_LITERAL_STRING("|") +
|
|
mService.mServiceType +
|
|
NS_LITERAL_STRING("|") +
|
|
NS_ConvertUTF8toUTF16(cHost) +
|
|
NS_LITERAL_STRING("|") +
|
|
mService.mDisplayName;
|
|
}
|
|
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo, int32_t aErrorCode)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnResolveFailed");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceRegistered");
|
|
|
|
nsCString cName;
|
|
if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString name = NS_ConvertUTF8toUTF16(cName);
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
|
|
if (!existingServer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
existingServer->PublishedServerStarted(NS_OK);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceUnregistered");
|
|
|
|
nsCString cName;
|
|
if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString name = NS_ConvertUTF8toUTF16(cName);
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
|
|
if (!existingServer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
LOG_I("OnServiceRegistered(MDNS): De-advertised server with name %s.", cName.get());
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnRegistrationFailed");
|
|
|
|
nsCString cName;
|
|
if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString name = NS_ConvertUTF8toUTF16(cName);
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
|
|
if (!existingServer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
LOG_I("OnServiceRegistered(MDNS): Registration of server with name %s failed.", cName.get());
|
|
|
|
// Remove the nsICancelable from the published server.
|
|
existingServer->PublishedServerStarted(NS_ERROR_FAILURE);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::OnUnregistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode)
|
|
{
|
|
LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnUnregistrationFailed");
|
|
|
|
nsCString cName;
|
|
if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsString name = NS_ConvertUTF8toUTF16(cName);
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
|
|
if (!existingServer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
LOG_I("OnServiceRegistered(MDNS): Un-Advertisement of server with name %s failed.", cName.get());
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::Notify(nsITimer* timer)
|
|
{
|
|
if (timer == mDiscoveryStopTimer.get()) {
|
|
LOG_I("MDNSService::Notify() got discovery stop timeout");
|
|
// Internet discovery stop timer has fired.
|
|
nsresult rv = StopDiscovery();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
if (timer == mDiscoveryStartTimer.get()) {
|
|
LOG_I("MDNSService::Notify() got discovery start timeout");
|
|
// Internet discovery start timer has fired.
|
|
nsresult rv = StartDiscovery();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
LOG_E("MDNSService::Notify got unknown timeout.");
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::Init()
|
|
{
|
|
MOZ_ASSERT(mDiscoveryState == DISCOVERY_IDLE);
|
|
|
|
mDiscoveryStartTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
if (!mDiscoveryStartTimer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mDiscoveryStopTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
if (!mDiscoveryStopTimer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult rv;
|
|
mDNSServiceDiscovery = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::StartDiscovery()
|
|
{
|
|
nsresult rv;
|
|
|
|
// Always cancel the timer.
|
|
rv = mDiscoveryStartTimer->Cancel();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
LOG_E("FlyWeb failed to cancel DNS service discovery start timer.");
|
|
}
|
|
|
|
// If discovery is not idle, don't start it.
|
|
if (mDiscoveryState != DISCOVERY_IDLE) {
|
|
return NS_OK;
|
|
}
|
|
|
|
LOG_I("FlyWeb starting dicovery.");
|
|
mDiscoveryState = DISCOVERY_STARTING;
|
|
|
|
// start the discovery.
|
|
rv = mDNSServiceDiscovery->StartDiscovery(mServiceType, this,
|
|
getter_AddRefs(mCancelDiscovery));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
LOG_E("FlyWeb failed to start DNS service discovery.");
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::StopDiscovery()
|
|
{
|
|
nsresult rv;
|
|
|
|
// Always cancel the timer.
|
|
rv = mDiscoveryStopTimer->Cancel();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
LOG_E("FlyWeb failed to cancel DNS service discovery stop timer.");
|
|
}
|
|
|
|
// If discovery is not running, do nothing.
|
|
if (mDiscoveryState != DISCOVERY_RUNNING) {
|
|
return NS_OK;
|
|
}
|
|
|
|
LOG_I("FlyWeb stopping dicovery.");
|
|
|
|
// Mark service discovery as stopping.
|
|
mDiscoveryState = DISCOVERY_STOPPING;
|
|
|
|
if (mCancelDiscovery) {
|
|
LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery exists!");
|
|
nsCOMPtr<nsICancelable> cancelDiscovery = mCancelDiscovery.forget();
|
|
rv = cancelDiscovery->Cancel(NS_ERROR_ABORT);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
LOG_E("FlyWeb failed to cancel DNS stop service discovery.");
|
|
}
|
|
} else {
|
|
LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery does not exist!");
|
|
mDiscoveryState = DISCOVERY_IDLE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
FlyWebMDNSService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices)
|
|
{
|
|
for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) {
|
|
aServices.AppendElement(iter.UserData()->mService);
|
|
}
|
|
}
|
|
|
|
bool
|
|
FlyWebMDNSService::HasService(const nsAString& aServiceId)
|
|
{
|
|
return mServiceMap.Contains(aServiceId);
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::PairWithService(const nsAString& aServiceId,
|
|
UniquePtr<FlyWebService::PairedInfo>& aInfo)
|
|
{
|
|
MOZ_ASSERT(HasService(aServiceId));
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
|
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsID id;
|
|
rv = uuidgen->GenerateUUIDInPlace(&id);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
aInfo.reset(new FlyWebService::PairedInfo());
|
|
|
|
char uuidChars[NSID_LENGTH];
|
|
id.ToProvidedString(uuidChars);
|
|
CopyUTF8toUTF16(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2),
|
|
aInfo->mService.mHostname);
|
|
|
|
DiscoveredInfo* discInfo = mServiceMap.Get(aServiceId);
|
|
|
|
nsAutoString url;
|
|
if (discInfo->mService.mCert.IsEmpty()) {
|
|
url.AssignLiteral("http://");
|
|
} else {
|
|
url.AssignLiteral("https://");
|
|
}
|
|
url.Append(aInfo->mService.mHostname + NS_LITERAL_STRING("/"));
|
|
nsCOMPtr<nsIURI> uiURL;
|
|
NS_NewURI(getter_AddRefs(uiURL), url);
|
|
MOZ_ASSERT(uiURL);
|
|
if (!discInfo->mService.mPath.IsEmpty()) {
|
|
nsCOMPtr<nsIURI> tmp = uiURL.forget();
|
|
NS_NewURI(getter_AddRefs(uiURL), discInfo->mService.mPath, nullptr, tmp);
|
|
}
|
|
if (uiURL) {
|
|
nsAutoCString spec;
|
|
uiURL->GetSpec(spec);
|
|
CopyUTF8toUTF16(spec, aInfo->mService.mUiUrl);
|
|
}
|
|
|
|
aInfo->mService.mDiscoveredService = discInfo->mService;
|
|
aInfo->mDNSServiceInfo = discInfo->mDNSServiceInfo;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FlyWebMDNSService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer)
|
|
{
|
|
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(aServer->Name());
|
|
MOZ_ASSERT(existingServer);
|
|
|
|
// Advertise the service via mdns.
|
|
RefPtr<net::nsDNSServiceInfo> serviceInfo(new net::nsDNSServiceInfo());
|
|
|
|
serviceInfo->SetPort(aServer->Port());
|
|
serviceInfo->SetServiceType(mServiceType);
|
|
|
|
nsCString certKey;
|
|
aServer->GetCertKey(certKey);
|
|
nsString uiURL;
|
|
aServer->GetUiUrl(uiURL);
|
|
|
|
if (!uiURL.IsEmpty() || !certKey.IsEmpty()) {
|
|
RefPtr<nsHashPropertyBag> attrs = new nsHashPropertyBag();
|
|
if (!uiURL.IsEmpty()) {
|
|
attrs->SetPropertyAsAString(NS_LITERAL_STRING("path"), uiURL);
|
|
}
|
|
if (!certKey.IsEmpty()) {
|
|
attrs->SetPropertyAsACString(NS_LITERAL_STRING("cert"), certKey);
|
|
}
|
|
serviceInfo->SetAttributes(attrs);
|
|
}
|
|
|
|
nsCString cstrName = NS_ConvertUTF16toUTF8(aServer->Name());
|
|
LOG_I("MDNSService::StartDiscoveryOf() advertising service %s", cstrName.get());
|
|
serviceInfo->SetServiceName(cstrName);
|
|
|
|
LogDNSInfo(serviceInfo, "FlyWebMDNSService::StartDiscoveryOf");
|
|
|
|
// Advertise the service.
|
|
nsCOMPtr<nsICancelable> cancelRegister;
|
|
nsresult rv = mDNSServiceDiscovery->
|
|
RegisterService(serviceInfo, this, getter_AddRefs(cancelRegister));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// All done.
|
|
aServer->SetCancelRegister(cancelRegister);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
FlyWebMDNSService::EnsureDiscoveryStarted()
|
|
{
|
|
mDiscoveryActive = true;
|
|
// If state is idle, start discovery immediately.
|
|
if (mDiscoveryState == DISCOVERY_IDLE) {
|
|
StartDiscovery();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlyWebMDNSService::EnsureDiscoveryStopped()
|
|
{
|
|
// All we need to do is set the flag to false.
|
|
// If current state is IDLE, it's already the correct state.
|
|
// Otherwise, the handlers for the internal state
|
|
// transitions will check this flag and drive the state
|
|
// towards IDLE.
|
|
mDiscoveryActive = false;
|
|
}
|
|
|
|
static StaticRefPtr<FlyWebService> gFlyWebService;
|
|
|
|
NS_IMPL_ISUPPORTS(FlyWebService, nsIObserver)
|
|
|
|
FlyWebService::FlyWebService()
|
|
: mMonitor("FlyWebService::mMonitor")
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->AddObserver(this, "inner-window-destroyed", false);
|
|
}
|
|
}
|
|
|
|
FlyWebService::~FlyWebService()
|
|
{
|
|
}
|
|
|
|
FlyWebService*
|
|
FlyWebService::GetExisting()
|
|
{
|
|
return gFlyWebService;
|
|
}
|
|
|
|
FlyWebService*
|
|
FlyWebService::GetOrCreate()
|
|
{
|
|
if (!gFlyWebService) {
|
|
gFlyWebService = new FlyWebService();
|
|
ClearOnShutdown(&gFlyWebService);
|
|
ErrorResult rv = gFlyWebService->Init();
|
|
if (rv.Failed()) {
|
|
gFlyWebService = nullptr;
|
|
return nullptr;
|
|
}
|
|
}
|
|
return gFlyWebService;
|
|
}
|
|
|
|
ErrorResult
|
|
FlyWebService::Init()
|
|
{
|
|
// Most functions of FlyWebService should not be started in the child.
|
|
// Instead FlyWebService in the child is mainly responsible for tracking
|
|
// publishedServer lifetimes. Other functions are handled by the
|
|
// FlyWebService running in the parent.
|
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
|
return ErrorResult(NS_OK);
|
|
}
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (!mMDNSHttpService) {
|
|
mMDNSHttpService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_http._tcp."));
|
|
ErrorResult rv;
|
|
|
|
rv = mMDNSHttpService->Init();
|
|
if (rv.Failed()) {
|
|
LOG_E("FlyWebService failed to initialize MDNS _http._tcp.");
|
|
mMDNSHttpService = nullptr;
|
|
rv.SuppressException();
|
|
}
|
|
}
|
|
|
|
if (!mMDNSFlywebService) {
|
|
mMDNSFlywebService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_flyweb._tcp."));
|
|
ErrorResult rv;
|
|
|
|
rv = mMDNSFlywebService->Init();
|
|
if (rv.Failed()) {
|
|
LOG_E("FlyWebService failed to initialize MDNS _flyweb._tcp.");
|
|
mMDNSFlywebService = nullptr;
|
|
rv.SuppressException();
|
|
}
|
|
}
|
|
|
|
return ErrorResult(NS_OK);
|
|
}
|
|
|
|
static already_AddRefed<FlyWebPublishPromise>
|
|
MakeRejectionPromise(const char* name)
|
|
{
|
|
MozPromiseHolder<FlyWebPublishPromise> holder;
|
|
RefPtr<FlyWebPublishPromise> promise = holder.Ensure(name);
|
|
holder.Reject(NS_ERROR_FAILURE, name);
|
|
return promise.forget();
|
|
}
|
|
|
|
static bool
|
|
CheckForFlyWebAddon(const nsACString& uriString)
|
|
{
|
|
// Before proceeding, ensure that the FlyWeb system addon exists.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURI> uri;
|
|
rv = NS_NewURI(getter_AddRefs(uri), uriString);
|
|
if (NS_FAILED(rv)) {
|
|
return false;
|
|
}
|
|
|
|
JSAddonId *addonId = MapURIToAddonID(uri);
|
|
if (!addonId) {
|
|
return false;
|
|
}
|
|
|
|
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
|
|
nsAutoString addonIdString;
|
|
AssignJSFlatString(addonIdString, flat);
|
|
if (!addonIdString.EqualsLiteral("flyweb@mozilla.org")) {
|
|
nsCString addonIdCString = NS_ConvertUTF16toUTF8(addonIdString);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<FlyWebPublishPromise>
|
|
FlyWebService::PublishServer(const nsAString& aName,
|
|
const FlyWebPublishOptions& aOptions,
|
|
nsPIDOMWindowInner* aWindow)
|
|
{
|
|
// Scan uiUrl for illegal characters
|
|
|
|
RefPtr<FlyWebPublishedServer> existingServer =
|
|
FlyWebService::GetOrCreate()->FindPublishedServerByName(aName);
|
|
if (existingServer) {
|
|
LOG_I("PublishServer: Trying to publish server with already-existing name %s.",
|
|
NS_ConvertUTF16toUTF8(aName).get());
|
|
return MakeRejectionPromise(__func__);
|
|
}
|
|
|
|
RefPtr<FlyWebPublishedServer> server;
|
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
|
server = new FlyWebPublishedServerChild(aWindow, aName, aOptions);
|
|
} else {
|
|
server = new FlyWebPublishedServerImpl(aWindow, aName, aOptions);
|
|
|
|
// Before proceeding, ensure that the FlyWeb system addon exists.
|
|
if (!CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/skin/icon-64.png")) &&
|
|
!CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/content/icon-64.png")))
|
|
{
|
|
LOG_E("PublishServer: Failed to find FlyWeb system addon.");
|
|
return MakeRejectionPromise(__func__);
|
|
}
|
|
}
|
|
|
|
if (aWindow) {
|
|
nsresult rv;
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
rv = NS_DispatchToCurrentThread(
|
|
MakeAndAddRef<FlyWebPublishServerPermissionCheck>(
|
|
NS_ConvertUTF16toUTF8(aName), aWindow->WindowID(), server));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
LOG_E("PublishServer: Failed to dispatch permission check runnable for %s",
|
|
NS_ConvertUTF16toUTF8(aName).get());
|
|
return MakeRejectionPromise(__func__);
|
|
}
|
|
} else {
|
|
// If aWindow is null, we're definitely in the e10s parent process.
|
|
// In this case, we know that permission has already been granted
|
|
// by the user because of content-process prompt.
|
|
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
|
server->PermissionGranted(true);
|
|
}
|
|
|
|
mServers.AppendElement(server);
|
|
|
|
return server->GetPublishPromise();
|
|
}
|
|
|
|
already_AddRefed<FlyWebPublishedServer>
|
|
FlyWebService::FindPublishedServerByName(
|
|
const nsAString& aName)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
for (FlyWebPublishedServer* publishedServer : mServers) {
|
|
if (publishedServer->Name().Equals(aName)) {
|
|
RefPtr<FlyWebPublishedServer> server = publishedServer;
|
|
return server.forget();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
FlyWebService::RegisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mDiscoveryManagerTable.PutEntry(aDiscoveryManager);
|
|
if (mMDNSHttpService) {
|
|
mMDNSHttpService->EnsureDiscoveryStarted();
|
|
}
|
|
if (mMDNSFlywebService) {
|
|
mMDNSFlywebService->EnsureDiscoveryStarted();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlyWebService::UnregisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mDiscoveryManagerTable.RemoveEntry(aDiscoveryManager);
|
|
if (mDiscoveryManagerTable.IsEmpty()) {
|
|
if (mMDNSHttpService) {
|
|
mMDNSHttpService->EnsureDiscoveryStopped();
|
|
}
|
|
if (mMDNSFlywebService) {
|
|
mMDNSFlywebService->EnsureDiscoveryStopped();
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FlyWebService::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (strcmp(aTopic, "inner-window-destroyed")) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
|
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
|
|
|
uint64_t innerID;
|
|
nsresult rv = wrapper->GetData(&innerID);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Make a copy of mServers to iterate over, because closing a server
|
|
// can remove entries from mServers.
|
|
nsCOMArray<FlyWebPublishedServer> serversCopy;
|
|
for (FlyWebPublishedServer* server : mServers) {
|
|
serversCopy.AppendElement(server);
|
|
}
|
|
|
|
for (FlyWebPublishedServer* server : serversCopy) {
|
|
if (server->OwnerWindowID() == innerID) {
|
|
server->Close();
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
FlyWebService::UnregisterServer(FlyWebPublishedServer* aServer)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
DebugOnly<bool> removed = mServers.RemoveElement(aServer);
|
|
MOZ_ASSERT(removed);
|
|
}
|
|
|
|
bool
|
|
FlyWebService::HasConnectionOrServer(uint64_t aWindowID)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
for (FlyWebPublishedServer* server : mServers) {
|
|
nsPIDOMWindowInner* win = server->GetOwner();
|
|
if (win && win->WindowID() == aWindowID) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
FlyWebService::NotifyDiscoveredServicesChanged()
|
|
{
|
|
// Process the service map, add to the pair map.
|
|
for (auto iter = mDiscoveryManagerTable.Iter(); !iter.Done(); iter.Next()) {
|
|
iter.Get()->GetKey()->NotifyDiscoveredServicesChanged();
|
|
}
|
|
}
|
|
|
|
void
|
|
FlyWebService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mMDNSHttpService) {
|
|
mMDNSHttpService->ListDiscoveredServices(aServices);
|
|
}
|
|
if (mMDNSFlywebService) {
|
|
mMDNSFlywebService->ListDiscoveredServices(aServices);
|
|
}
|
|
}
|
|
|
|
void
|
|
FlyWebService::PairWithService(const nsAString& aServiceId,
|
|
FlyWebPairingCallback& aCallback)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
// See if we have already paired with this service. If so, re-use the
|
|
// FlyWebPairedService for that.
|
|
{
|
|
ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
|
|
for (auto iter = mPairedServiceTable.Iter(); !iter.Done(); iter.Next()) {
|
|
PairedInfo* pairInfo = iter.UserData();
|
|
if (pairInfo->mService.mDiscoveredService.mServiceId.Equals(aServiceId)) {
|
|
ErrorResult er;
|
|
ReentrantMonitorAutoExit pairedMapRelease(mMonitor);
|
|
aCallback.PairingSucceeded(pairInfo->mService, er);
|
|
ENSURE_SUCCESS_VOID(er);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
UniquePtr<PairedInfo> pairInfo;
|
|
|
|
nsresult rv = NS_OK;
|
|
bool notFound = false;
|
|
if (mMDNSHttpService && mMDNSHttpService->HasService(aServiceId)) {
|
|
rv = mMDNSHttpService->PairWithService(aServiceId, pairInfo);
|
|
} else if (mMDNSFlywebService && mMDNSFlywebService->HasService(aServiceId)) {
|
|
rv = mMDNSFlywebService->PairWithService(aServiceId, pairInfo);
|
|
} else {
|
|
notFound = true;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
ErrorResult result;
|
|
result.ThrowWithCustomCleanup(rv);
|
|
const nsAString& reason = NS_LITERAL_STRING("Error pairing.");
|
|
aCallback.PairingFailed(reason, result);
|
|
ENSURE_SUCCESS_VOID(result);
|
|
return;
|
|
}
|
|
|
|
if (!pairInfo) {
|
|
ErrorResult res;
|
|
const nsAString& reason = notFound ?
|
|
NS_LITERAL_STRING("No such service.") :
|
|
NS_LITERAL_STRING("Error pairing.");
|
|
aCallback.PairingFailed(reason, res);
|
|
ENSURE_SUCCESS_VOID(res);
|
|
return;
|
|
}
|
|
|
|
// Add fingerprint to certificate override database.
|
|
if (!pairInfo->mService.mDiscoveredService.mCert.IsEmpty()) {
|
|
nsCOMPtr<nsICertOverrideService> override =
|
|
do_GetService("@mozilla.org/security/certoverride;1");
|
|
if (!override ||
|
|
NS_FAILED(override->RememberTemporaryValidityOverrideUsingFingerprint(
|
|
NS_ConvertUTF16toUTF8(pairInfo->mService.mHostname),
|
|
-1,
|
|
NS_ConvertUTF16toUTF8(pairInfo->mService.mDiscoveredService.mCert),
|
|
nsICertOverrideService::ERROR_UNTRUSTED |
|
|
nsICertOverrideService::ERROR_MISMATCH))) {
|
|
ErrorResult res;
|
|
aCallback.PairingFailed(NS_LITERAL_STRING("Error adding certificate override."), res);
|
|
ENSURE_SUCCESS_VOID(res);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Grab a weak reference to the PairedInfo so that we can
|
|
// use it even after ownership has been transferred to mPairedServiceTable
|
|
PairedInfo* pairInfoWeak = pairInfo.release();
|
|
|
|
{
|
|
ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
|
|
mPairedServiceTable.Put(
|
|
NS_ConvertUTF16toUTF8(pairInfoWeak->mService.mHostname), pairInfoWeak);
|
|
}
|
|
|
|
ErrorResult er;
|
|
aCallback.PairingSucceeded(pairInfoWeak->mService, er);
|
|
ENSURE_SUCCESS_VOID(er);
|
|
}
|
|
|
|
nsresult
|
|
FlyWebService::CreateTransportForHost(const char **types,
|
|
uint32_t typeCount,
|
|
const nsACString &host,
|
|
int32_t port,
|
|
const nsACString &hostRoute,
|
|
int32_t portRoute,
|
|
nsIProxyInfo *proxyInfo,
|
|
nsISocketTransport **result)
|
|
{
|
|
// This might be called on background threads
|
|
|
|
*result = nullptr;
|
|
|
|
nsCString ipAddrString;
|
|
uint16_t discPort;
|
|
|
|
{
|
|
ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
|
|
|
|
PairedInfo* info = mPairedServiceTable.Get(host);
|
|
|
|
if (!info) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Get the ip address of the underlying service.
|
|
info->mDNSServiceInfo->GetAddress(ipAddrString);
|
|
info->mDNSServiceInfo->GetPort(&discPort);
|
|
}
|
|
|
|
// Parse it into an NetAddr.
|
|
PRNetAddr prNetAddr;
|
|
PRStatus status = PR_StringToNetAddr(ipAddrString.get(), &prNetAddr);
|
|
NS_ENSURE_FALSE(status == PR_FAILURE, NS_ERROR_FAILURE);
|
|
|
|
// Convert PRNetAddr to NetAddr.
|
|
mozilla::net::NetAddr netAddr;
|
|
PRNetAddrToNetAddr(&prNetAddr, &netAddr);
|
|
netAddr.inet.port = htons(discPort);
|
|
|
|
RefPtr<mozilla::net::nsSocketTransport> trans = new mozilla::net::nsSocketTransport();
|
|
nsresult rv = trans->InitPreResolved(
|
|
types, typeCount, host, port, hostRoute, portRoute, proxyInfo, &netAddr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
trans.forget(result);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
FlyWebService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsresult rv = mMDNSFlywebService ?
|
|
mMDNSFlywebService->StartDiscoveryOf(aServer) :
|
|
NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(rv)) {
|
|
aServer->PublishedServerStarted(rv);
|
|
}
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|