mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-31 11:37:30 +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
330
netwerk/dns/ChildDNSService.cpp
Normal file
330
netwerk/dns/ChildDNSService.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/* 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/net/ChildDNSService.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/DNSListenerProxy.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static ChildDNSService *gChildDNSService;
|
||||
static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
|
||||
|
||||
ChildDNSService* ChildDNSService::GetSingleton()
|
||||
{
|
||||
MOZ_ASSERT(IsNeckoChild());
|
||||
|
||||
if (!gChildDNSService) {
|
||||
gChildDNSService = new ChildDNSService();
|
||||
}
|
||||
|
||||
NS_ADDREF(gChildDNSService);
|
||||
return gChildDNSService;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ChildDNSService,
|
||||
nsIDNSService,
|
||||
nsPIDNSService,
|
||||
nsIObserver)
|
||||
|
||||
ChildDNSService::ChildDNSService()
|
||||
: mFirstTime(true)
|
||||
, mDisablePrefetch(false)
|
||||
, mPendingRequestsLock("DNSPendingRequestsLock")
|
||||
{
|
||||
MOZ_ASSERT(IsNeckoChild());
|
||||
}
|
||||
|
||||
ChildDNSService::~ChildDNSService()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
|
||||
uint32_t aFlags,
|
||||
const nsACString &aNetworkInterface,
|
||||
nsIDNSListener* aListener,
|
||||
nsACString &aHashKey)
|
||||
{
|
||||
aHashKey.Assign(aHost);
|
||||
aHashKey.AppendInt(aFlags);
|
||||
if (!aNetworkInterface.IsEmpty()) {
|
||||
aHashKey.Append(aNetworkInterface);
|
||||
}
|
||||
aHashKey.AppendPrintf("%p", aListener);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsIDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::AsyncResolve(const nsACString &hostname,
|
||||
uint32_t flags,
|
||||
nsIDNSListener *listener,
|
||||
nsIEventTarget *target_,
|
||||
nsICancelable **result)
|
||||
{
|
||||
return AsyncResolveExtended(hostname, flags, EmptyCString(), listener,
|
||||
target_, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::AsyncResolveExtended(const nsACString &hostname,
|
||||
uint32_t flags,
|
||||
const nsACString &aNetworkInterface,
|
||||
nsIDNSListener *listener,
|
||||
nsIEventTarget *target_,
|
||||
nsICancelable **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
|
||||
|
||||
if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
|
||||
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
// We need original flags for the pending requests hash.
|
||||
uint32_t originalFlags = flags;
|
||||
|
||||
// Support apps being 'offline' even if parent is not: avoids DNS traffic by
|
||||
// apps that have been told they are offline.
|
||||
if (GetOffline()) {
|
||||
flags |= RESOLVE_OFFLINE;
|
||||
}
|
||||
|
||||
// We need original listener for the pending requests hash.
|
||||
nsIDNSListener *originalListener = listener;
|
||||
|
||||
// make sure JS callers get notification on the main thread
|
||||
nsCOMPtr<nsIEventTarget> target = target_;
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
|
||||
if (wrappedListener && !target) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
target = do_QueryInterface(mainThread);
|
||||
}
|
||||
if (target) {
|
||||
// Guarantee listener freed on main thread. Not sure we need this in child
|
||||
// (or in parent in nsDNSService.cpp) but doesn't hurt.
|
||||
listener = new DNSListenerProxy(listener, target);
|
||||
}
|
||||
|
||||
RefPtr<DNSRequestChild> childReq =
|
||||
new DNSRequestChild(nsCString(hostname), flags,
|
||||
nsCString(aNetworkInterface),
|
||||
listener, target);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mPendingRequestsLock);
|
||||
nsCString key;
|
||||
GetDNSRecordHashKey(hostname, originalFlags, aNetworkInterface,
|
||||
originalListener, key);
|
||||
nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
|
||||
if (mPendingRequests.Get(key, &hashEntry)) {
|
||||
hashEntry->AppendElement(childReq);
|
||||
} else {
|
||||
hashEntry = new nsTArray<RefPtr<DNSRequestChild>>();
|
||||
hashEntry->AppendElement(childReq);
|
||||
mPendingRequests.Put(key, hashEntry);
|
||||
}
|
||||
}
|
||||
|
||||
childReq->StartRequest();
|
||||
|
||||
childReq.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
|
||||
uint32_t aFlags,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult aReason)
|
||||
{
|
||||
return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(),
|
||||
aListener, aReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
|
||||
uint32_t aFlags,
|
||||
const nsACString &aNetworkInterface,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult aReason)
|
||||
{
|
||||
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
|
||||
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mPendingRequestsLock);
|
||||
nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
|
||||
nsCString key;
|
||||
GetDNSRecordHashKey(aHostname, aFlags, aNetworkInterface, aListener, key);
|
||||
if (mPendingRequests.Get(key, &hashEntry)) {
|
||||
// We cancel just one.
|
||||
hashEntry->ElementAt(0)->Cancel(aReason);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::Resolve(const nsACString &hostname,
|
||||
uint32_t flags,
|
||||
nsIDNSRecord **result)
|
||||
{
|
||||
// not planning to ever support this, since sync IPDL is evil.
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
|
||||
{
|
||||
// Only used by networking dashboard, so may not ever need this in child.
|
||||
// (and would provide a way to spy on what hosts other apps are connecting to,
|
||||
// unless we start keeping per-app DNS caches).
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetMyHostName(nsACString &result)
|
||||
{
|
||||
// TODO: get value from parent during PNecko construction?
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
void
|
||||
ChildDNSService::NotifyRequestDone(DNSRequestChild *aDnsRequest)
|
||||
{
|
||||
// We need the original flags and listener for the pending requests hash.
|
||||
uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
|
||||
nsCOMPtr<nsIDNSListener> originalListener = aDnsRequest->mListener;
|
||||
nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(originalListener);
|
||||
if (wrapper) {
|
||||
wrapper->GetOriginalListener(getter_AddRefs(originalListener));
|
||||
if (NS_WARN_IF(!originalListener)) {
|
||||
MOZ_ASSERT(originalListener);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mPendingRequestsLock);
|
||||
|
||||
nsCString key;
|
||||
GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags,
|
||||
aDnsRequest->mNetworkInterface, originalListener, key);
|
||||
|
||||
nsTArray<RefPtr<DNSRequestChild>> *hashEntry;
|
||||
|
||||
if (mPendingRequests.Get(key, &hashEntry)) {
|
||||
int idx;
|
||||
if ((idx = hashEntry->IndexOf(aDnsRequest))) {
|
||||
hashEntry->RemoveElementAt(idx);
|
||||
if (hashEntry->IsEmpty()) {
|
||||
mPendingRequests.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsPIDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
ChildDNSService::Init()
|
||||
{
|
||||
// Disable prefetching either by explicit preference or if a manual proxy
|
||||
// is configured
|
||||
bool disablePrefetch = false;
|
||||
int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs) {
|
||||
prefs->GetIntPref("network.proxy.type", &proxyType);
|
||||
prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
|
||||
}
|
||||
|
||||
if (mFirstTime) {
|
||||
mFirstTime = false;
|
||||
if (prefs) {
|
||||
prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
|
||||
|
||||
// Monitor these to see if there is a change in proxy configuration
|
||||
// If a manual proxy is in use, disable prefetch implicitly
|
||||
prefs->AddObserver("network.proxy.type", this, false);
|
||||
}
|
||||
}
|
||||
|
||||
mDisablePrefetch = disablePrefetch ||
|
||||
(proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChildDNSService::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetPrefetchEnabled(bool *outVal)
|
||||
{
|
||||
*outVal = !mDisablePrefetch;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::SetPrefetchEnabled(bool inVal)
|
||||
{
|
||||
mDisablePrefetch = !inVal;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ChildDNSService::GetOffline() const
|
||||
{
|
||||
bool offline = false;
|
||||
nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
|
||||
if (io) {
|
||||
io->GetOffline(&offline);
|
||||
}
|
||||
return offline;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsIObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::Observe(nsISupports *subject, const char *topic,
|
||||
const char16_t *data)
|
||||
{
|
||||
// we are only getting called if a preference has changed.
|
||||
NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
|
||||
"unexpected observe call");
|
||||
|
||||
// Reread prefs
|
||||
Init();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
60
netwerk/dns/ChildDNSService.h
Normal file
60
netwerk/dns/ChildDNSService.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#ifndef mozilla_net_ChildDNSService_h
|
||||
#define mozilla_net_ChildDNSService_h
|
||||
|
||||
|
||||
#include "nsPIDNSService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "DNSRequestChild.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsClassHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ChildDNSService final
|
||||
: public nsPIDNSService
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
// AsyncResolve (and CancelAsyncResolve) can be called off-main
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSPIDNSSERVICE
|
||||
NS_DECL_NSIDNSSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
ChildDNSService();
|
||||
|
||||
static ChildDNSService* GetSingleton();
|
||||
|
||||
void NotifyRequestDone(DNSRequestChild *aDnsRequest);
|
||||
|
||||
bool GetOffline() const;
|
||||
private:
|
||||
virtual ~ChildDNSService();
|
||||
|
||||
void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(const nsACString &aHost,
|
||||
uint32_t aFlags,
|
||||
const nsACString &aNetworkInterface,
|
||||
nsIDNSListener* aListener,
|
||||
nsACString &aHashKey);
|
||||
|
||||
bool mFirstTime;
|
||||
bool mDisablePrefetch;
|
||||
|
||||
// We need to remember pending dns requests to be able to cancel them.
|
||||
nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestChild>>> mPendingRequests;
|
||||
Mutex mPendingRequestsLock;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_ChildDNSService_h
|
||||
368
netwerk/dns/DNS.cpp
Normal file
368
netwerk/dns/DNS.cpp
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
/* 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/net/DNS.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "ws2tcpip.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
const char *inet_ntop_internal(int af, const void *src, char *dst, socklen_t size)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
if (af == AF_INET) {
|
||||
struct sockaddr_in s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin_family = AF_INET;
|
||||
memcpy(&s.sin_addr, src, sizeof(struct in_addr));
|
||||
int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in),
|
||||
dst, size, nullptr, 0, NI_NUMERICHOST);
|
||||
if (result == 0) {
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
else if (af == AF_INET6) {
|
||||
struct sockaddr_in6 s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin6_family = AF_INET6;
|
||||
memcpy(&s.sin6_addr, src, sizeof(struct in_addr6));
|
||||
int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in6),
|
||||
dst, size, nullptr, 0, NI_NUMERICHOST);
|
||||
if (result == 0) {
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
#else
|
||||
return inet_ntop(af, src, dst, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Copies the contents of a PRNetAddr to a NetAddr.
|
||||
// Does not do a ptr safety check!
|
||||
void PRNetAddrToNetAddr(const PRNetAddr *prAddr, NetAddr *addr)
|
||||
{
|
||||
if (prAddr->raw.family == PR_AF_INET) {
|
||||
addr->inet.family = AF_INET;
|
||||
addr->inet.port = prAddr->inet.port;
|
||||
addr->inet.ip = prAddr->inet.ip;
|
||||
}
|
||||
else if (prAddr->raw.family == PR_AF_INET6) {
|
||||
addr->inet6.family = AF_INET6;
|
||||
addr->inet6.port = prAddr->ipv6.port;
|
||||
addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
|
||||
memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
|
||||
addr->inet6.scope_id = prAddr->ipv6.scope_id;
|
||||
}
|
||||
#if defined(XP_UNIX)
|
||||
else if (prAddr->raw.family == PR_AF_LOCAL) {
|
||||
addr->local.family = AF_LOCAL;
|
||||
memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Copies the contents of a NetAddr to a PRNetAddr.
|
||||
// Does not do a ptr safety check!
|
||||
void NetAddrToPRNetAddr(const NetAddr *addr, PRNetAddr *prAddr)
|
||||
{
|
||||
if (addr->raw.family == AF_INET) {
|
||||
prAddr->inet.family = PR_AF_INET;
|
||||
prAddr->inet.port = addr->inet.port;
|
||||
prAddr->inet.ip = addr->inet.ip;
|
||||
}
|
||||
else if (addr->raw.family == AF_INET6) {
|
||||
prAddr->ipv6.family = PR_AF_INET6;
|
||||
prAddr->ipv6.port = addr->inet6.port;
|
||||
prAddr->ipv6.flowinfo = addr->inet6.flowinfo;
|
||||
memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
|
||||
prAddr->ipv6.scope_id = addr->inet6.scope_id;
|
||||
}
|
||||
#if defined(XP_UNIX)
|
||||
else if (addr->raw.family == AF_LOCAL) {
|
||||
prAddr->local.family = PR_AF_LOCAL;
|
||||
memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
|
||||
}
|
||||
#elif defined(XP_WIN)
|
||||
else if (addr->raw.family == AF_LOCAL) {
|
||||
prAddr->local.family = PR_AF_LOCAL;
|
||||
memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize)
|
||||
{
|
||||
if (addr->raw.family == AF_INET) {
|
||||
if (bufSize < INET_ADDRSTRLEN) {
|
||||
return false;
|
||||
}
|
||||
struct in_addr nativeAddr = {};
|
||||
nativeAddr.s_addr = addr->inet.ip;
|
||||
return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize);
|
||||
}
|
||||
else if (addr->raw.family == AF_INET6) {
|
||||
if (bufSize < INET6_ADDRSTRLEN) {
|
||||
return false;
|
||||
}
|
||||
struct in6_addr nativeAddr = {};
|
||||
memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
|
||||
return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize);
|
||||
}
|
||||
#if defined(XP_UNIX)
|
||||
else if (addr->raw.family == AF_LOCAL) {
|
||||
if (bufSize < sizeof(addr->local.path)) {
|
||||
// Many callers don't bother checking our return value, so
|
||||
// null-terminate just in case.
|
||||
if (bufSize > 0) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Usually, the size passed to memcpy should be the size of the
|
||||
// destination. Here, we know that the source is no larger than the
|
||||
// destination, so using the source's size is always safe, whereas
|
||||
// using the destination's size may cause us to read off the end of the
|
||||
// source.
|
||||
memcpy(buf, addr->local.path, sizeof(addr->local.path));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLoopBackAddress(const NetAddr *addr)
|
||||
{
|
||||
if (addr->raw.family == AF_INET) {
|
||||
return (addr->inet.ip == htonl(INADDR_LOOPBACK));
|
||||
}
|
||||
else if (addr->raw.family == AF_INET6) {
|
||||
if (IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip)) {
|
||||
return true;
|
||||
} else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
|
||||
IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsIPAddrAny(const NetAddr *addr)
|
||||
{
|
||||
if (addr->raw.family == AF_INET) {
|
||||
if (addr->inet.ip == htonl(INADDR_ANY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (addr->raw.family == AF_INET6) {
|
||||
if (IPv6ADDR_IS_UNSPECIFIED(&addr->inet6.ip)) {
|
||||
return true;
|
||||
} else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
|
||||
IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_ANY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsIPAddrV4Mapped(const NetAddr *addr)
|
||||
{
|
||||
if (addr->raw.family == AF_INET6) {
|
||||
return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsIPAddrLocal(const NetAddr *addr)
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
|
||||
// IPv4 RFC1918 and Link Local Addresses.
|
||||
if (addr->raw.family == AF_INET) {
|
||||
uint32_t addr32 = ntohl(addr->inet.ip);
|
||||
if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918).
|
||||
addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918).
|
||||
addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918).
|
||||
addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local).
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// IPv6 Unique and Link Local Addresses.
|
||||
if (addr->raw.family == AF_INET6) {
|
||||
uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);
|
||||
if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address.
|
||||
addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Not an IPv4/6 local address.
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetPort(const NetAddr *aAddr, uint16_t *aResult)
|
||||
{
|
||||
uint16_t port;
|
||||
if (aAddr->raw.family == PR_AF_INET) {
|
||||
port = aAddr->inet.port;
|
||||
} else if (aAddr->raw.family == PR_AF_INET6) {
|
||||
port = aAddr->inet6.port;
|
||||
} else {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
*aResult = ntohs(port);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
NetAddr::operator == (const NetAddr& other) const
|
||||
{
|
||||
if (this->raw.family != other.raw.family) {
|
||||
return false;
|
||||
} else if (this->raw.family == AF_INET) {
|
||||
return (this->inet.port == other.inet.port) &&
|
||||
(this->inet.ip == other.inet.ip);
|
||||
} else if (this->raw.family == AF_INET6) {
|
||||
return (this->inet6.port == other.inet6.port) &&
|
||||
(this->inet6.flowinfo == other.inet6.flowinfo) &&
|
||||
(memcmp(&this->inet6.ip, &other.inet6.ip,
|
||||
sizeof(this->inet6.ip)) == 0) &&
|
||||
(this->inet6.scope_id == other.inet6.scope_id);
|
||||
#if defined(XP_UNIX)
|
||||
} else if (this->raw.family == AF_LOCAL) {
|
||||
return PL_strncmp(this->local.path, other.local.path,
|
||||
ArrayLength(this->local.path));
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
NetAddr::operator < (const NetAddr& other) const
|
||||
{
|
||||
if (this->raw.family != other.raw.family) {
|
||||
return this->raw.family < other.raw.family;
|
||||
} else if (this->raw.family == AF_INET) {
|
||||
if (this->inet.ip == other.inet.ip) {
|
||||
return this->inet.port < other.inet.port;
|
||||
} else {
|
||||
return this->inet.ip < other.inet.ip;
|
||||
}
|
||||
} else if (this->raw.family == AF_INET6) {
|
||||
int cmpResult = memcmp(&this->inet6.ip, &other.inet6.ip,
|
||||
sizeof(this->inet6.ip));
|
||||
if (cmpResult) {
|
||||
return cmpResult < 0;
|
||||
} else if (this->inet6.port != other.inet6.port) {
|
||||
return this->inet6.port < other.inet6.port;
|
||||
} else {
|
||||
return this->inet6.flowinfo < other.inet6.flowinfo;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
|
||||
{
|
||||
PRNetAddrToNetAddr(prNetAddr, &mAddress);
|
||||
}
|
||||
|
||||
NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
|
||||
{
|
||||
mAddress = netAddr.mAddress;
|
||||
}
|
||||
|
||||
NetAddrElement::~NetAddrElement() = default;
|
||||
|
||||
AddrInfo::AddrInfo(const char *host, const PRAddrInfo *prAddrInfo,
|
||||
bool disableIPv4, bool filterNameCollision, const char *cname)
|
||||
: mHostName(nullptr)
|
||||
, mCanonicalName(nullptr)
|
||||
, ttl(NO_TTL_DATA)
|
||||
{
|
||||
MOZ_ASSERT(prAddrInfo, "Cannot construct AddrInfo with a null prAddrInfo pointer!");
|
||||
const uint32_t nameCollisionAddr = htonl(0x7f003535); // 127.0.53.53
|
||||
|
||||
Init(host, cname);
|
||||
PRNetAddr tmpAddr;
|
||||
void *iter = nullptr;
|
||||
do {
|
||||
iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr);
|
||||
bool addIt = iter &&
|
||||
(!disableIPv4 || tmpAddr.raw.family != PR_AF_INET) &&
|
||||
(!filterNameCollision || tmpAddr.raw.family != PR_AF_INET || (tmpAddr.inet.ip != nameCollisionAddr));
|
||||
if (addIt) {
|
||||
auto *addrElement = new NetAddrElement(&tmpAddr);
|
||||
mAddresses.insertBack(addrElement);
|
||||
}
|
||||
} while (iter);
|
||||
}
|
||||
|
||||
AddrInfo::AddrInfo(const char *host, const char *cname)
|
||||
: mHostName(nullptr)
|
||||
, mCanonicalName(nullptr)
|
||||
, ttl(NO_TTL_DATA)
|
||||
{
|
||||
Init(host, cname);
|
||||
}
|
||||
|
||||
AddrInfo::~AddrInfo()
|
||||
{
|
||||
NetAddrElement *addrElement;
|
||||
while ((addrElement = mAddresses.popLast())) {
|
||||
delete addrElement;
|
||||
}
|
||||
free(mHostName);
|
||||
free(mCanonicalName);
|
||||
}
|
||||
|
||||
void
|
||||
AddrInfo::Init(const char *host, const char *cname)
|
||||
{
|
||||
MOZ_ASSERT(host, "Cannot initialize AddrInfo with a null host pointer!");
|
||||
|
||||
ttl = NO_TTL_DATA;
|
||||
size_t hostlen = strlen(host);
|
||||
mHostName = static_cast<char*>(moz_xmalloc(hostlen + 1));
|
||||
memcpy(mHostName, host, hostlen + 1);
|
||||
if (cname) {
|
||||
size_t cnameLen = strlen(cname);
|
||||
mCanonicalName = static_cast<char*>(moz_xmalloc(cnameLen + 1));
|
||||
memcpy(mCanonicalName, cname, cnameLen + 1);
|
||||
}
|
||||
else {
|
||||
mCanonicalName = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AddrInfo::AddAddress(NetAddrElement *address)
|
||||
{
|
||||
MOZ_ASSERT(address, "Cannot add the address to an uninitialized list");
|
||||
|
||||
mAddresses.insertBack(address);
|
||||
}
|
||||
|
||||
size_t
|
||||
AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
size_t n = mallocSizeOf(this);
|
||||
n += mallocSizeOf(mHostName);
|
||||
n += mallocSizeOf(mCanonicalName);
|
||||
n += mAddresses.sizeOfExcludingThis(mallocSizeOf);
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
183
netwerk/dns/DNS.h
Normal file
183
netwerk/dns/DNS.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef DNS_h_
|
||||
#define DNS_h_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "prio.h"
|
||||
#include "prnetdb.h"
|
||||
#include "plstr.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#if !defined(XP_WIN)
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "winsock2.h"
|
||||
#endif
|
||||
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL 1 // used for named pipe
|
||||
#endif
|
||||
|
||||
#define IPv6ADDR_IS_LOOPBACK(a) \
|
||||
(((a)->u32[0] == 0) && \
|
||||
((a)->u32[1] == 0) && \
|
||||
((a)->u32[2] == 0) && \
|
||||
((a)->u8[12] == 0) && \
|
||||
((a)->u8[13] == 0) && \
|
||||
((a)->u8[14] == 0) && \
|
||||
((a)->u8[15] == 0x1U))
|
||||
|
||||
#define IPv6ADDR_IS_V4MAPPED(a) \
|
||||
(((a)->u32[0] == 0) && \
|
||||
((a)->u32[1] == 0) && \
|
||||
((a)->u8[8] == 0) && \
|
||||
((a)->u8[9] == 0) && \
|
||||
((a)->u8[10] == 0xff) && \
|
||||
((a)->u8[11] == 0xff))
|
||||
|
||||
#define IPv6ADDR_V4MAPPED_TO_IPADDR(a) ((a)->u32[3])
|
||||
|
||||
#define IPv6ADDR_IS_UNSPECIFIED(a) \
|
||||
(((a)->u32[0] == 0) && \
|
||||
((a)->u32[1] == 0) && \
|
||||
((a)->u32[2] == 0) && \
|
||||
((a)->u32[3] == 0))
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Required buffer size for text form of an IP address.
|
||||
// Includes space for null termination. We make our own contants
|
||||
// because we don't want higher-level code depending on things
|
||||
// like INET6_ADDRSTRLEN and having to include the associated
|
||||
// platform-specific headers.
|
||||
#ifdef XP_WIN
|
||||
// Windows requires longer buffers for some reason.
|
||||
const int kIPv4CStrBufSize = 22;
|
||||
const int kIPv6CStrBufSize = 65;
|
||||
const int kNetAddrMaxCStrBufSize = kIPv6CStrBufSize;
|
||||
#else
|
||||
const int kIPv4CStrBufSize = 16;
|
||||
const int kIPv6CStrBufSize = 46;
|
||||
const int kLocalCStrBufSize = 108;
|
||||
const int kNetAddrMaxCStrBufSize = kLocalCStrBufSize;
|
||||
#endif
|
||||
|
||||
// This was all created at a time in which we were using NSPR for host
|
||||
// resolution and we were propagating NSPR types like "PRAddrInfo" and
|
||||
// "PRNetAddr" all over Gecko. This made it hard to use another host
|
||||
// resolver -- we were locked into NSPR. The goal here is to get away
|
||||
// from that. We'll translate what we get from NSPR or any other host
|
||||
// resolution library into the types below and use them in Gecko.
|
||||
|
||||
union IPv6Addr {
|
||||
uint8_t u8[16];
|
||||
uint16_t u16[8];
|
||||
uint32_t u32[4];
|
||||
uint64_t u64[2];
|
||||
};
|
||||
|
||||
// This struct is similar to operating system structs like "sockaddr", used for
|
||||
// things like "connect" and "getsockname". When tempted to cast or do dumb
|
||||
// copies of this struct to another struct, bear compiler-computed padding
|
||||
// in mind. The size of this struct, and the layout of the data in it, may
|
||||
// not be what you expect.
|
||||
union NetAddr {
|
||||
struct {
|
||||
uint16_t family; /* address family (0x00ff maskable) */
|
||||
char data[14]; /* raw address data */
|
||||
} raw;
|
||||
struct {
|
||||
uint16_t family; /* address family (AF_INET) */
|
||||
uint16_t port; /* port number */
|
||||
uint32_t ip; /* The actual 32 bits of address */
|
||||
} inet;
|
||||
struct {
|
||||
uint16_t family; /* address family (AF_INET6) */
|
||||
uint16_t port; /* port number */
|
||||
uint32_t flowinfo; /* routing information */
|
||||
IPv6Addr ip; /* the actual 128 bits of address */
|
||||
uint32_t scope_id; /* set of interfaces for a scope */
|
||||
} inet6;
|
||||
#if defined(XP_UNIX) || defined(XP_WIN)
|
||||
struct { /* Unix domain socket or
|
||||
Windows Named Pipes address */
|
||||
uint16_t family; /* address family (AF_UNIX) */
|
||||
char path[104]; /* null-terminated pathname */
|
||||
} local;
|
||||
#endif
|
||||
// introduced to support nsTArray<NetAddr> comparisons and sorting
|
||||
bool operator == (const NetAddr& other) const;
|
||||
bool operator < (const NetAddr &other) const;
|
||||
};
|
||||
|
||||
// This class wraps a NetAddr union to provide C++ linked list
|
||||
// capabilities and other methods. It is created from a PRNetAddr,
|
||||
// which is converted to a mozilla::dns::NetAddr.
|
||||
class NetAddrElement : public LinkedListElement<NetAddrElement> {
|
||||
public:
|
||||
explicit NetAddrElement(const PRNetAddr *prNetAddr);
|
||||
NetAddrElement(const NetAddrElement& netAddr);
|
||||
~NetAddrElement();
|
||||
|
||||
NetAddr mAddress;
|
||||
};
|
||||
|
||||
class AddrInfo {
|
||||
public:
|
||||
// Creates an AddrInfo object. It calls the AddrInfo(const char*, const char*)
|
||||
// to initialize the host and the cname.
|
||||
AddrInfo(const char *host, const PRAddrInfo *prAddrInfo, bool disableIPv4,
|
||||
bool filterNameCollision, const char *cname);
|
||||
|
||||
// Creates a basic AddrInfo object (initialize only the host and the cname).
|
||||
AddrInfo(const char *host, const char *cname);
|
||||
~AddrInfo();
|
||||
|
||||
void AddAddress(NetAddrElement *address);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
char *mHostName;
|
||||
char *mCanonicalName;
|
||||
uint16_t ttl;
|
||||
static const uint16_t NO_TTL_DATA = (uint16_t) -1;
|
||||
|
||||
LinkedList<NetAddrElement> mAddresses;
|
||||
|
||||
private:
|
||||
void Init(const char *host, const char *cname);
|
||||
};
|
||||
|
||||
// Copies the contents of a PRNetAddr to a NetAddr.
|
||||
// Does not do a ptr safety check!
|
||||
void PRNetAddrToNetAddr(const PRNetAddr *prAddr, NetAddr *addr);
|
||||
|
||||
// Copies the contents of a NetAddr to a PRNetAddr.
|
||||
// Does not do a ptr safety check!
|
||||
void NetAddrToPRNetAddr(const NetAddr *addr, PRNetAddr *prAddr);
|
||||
|
||||
bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize);
|
||||
|
||||
bool IsLoopBackAddress(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrAny(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrV4Mapped(const NetAddr *addr);
|
||||
|
||||
bool IsIPAddrLocal(const NetAddr *addr);
|
||||
|
||||
nsresult GetPort(const NetAddr *aAddr, uint16_t *aResult);
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DNS_h_
|
||||
43
netwerk/dns/DNSListenerProxy.cpp
Normal file
43
netwerk/dns/DNSListenerProxy.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et 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/net/DNSListenerProxy.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS(DNSListenerProxy,
|
||||
nsIDNSListener,
|
||||
nsIDNSListenerProxy)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
{
|
||||
RefPtr<OnLookupCompleteRunnable> r =
|
||||
new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
|
||||
return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupCompleteRunnable::Run()
|
||||
{
|
||||
mListener->OnLookupComplete(mRequest, mRecord, mStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::GetOriginalListener(nsIDNSListener **aOriginalListener)
|
||||
{
|
||||
NS_IF_ADDREF(*aOriginalListener = mListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
72
netwerk/dns/DNSListenerProxy.h
Normal file
72
netwerk/dns/DNSListenerProxy.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et 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/. */
|
||||
|
||||
#ifndef DNSListenerProxy_h__
|
||||
#define DNSListenerProxy_h__
|
||||
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsIEventTarget;
|
||||
class nsICancelable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSListenerProxy final
|
||||
: public nsIDNSListener
|
||||
, public nsIDNSListenerProxy
|
||||
{
|
||||
public:
|
||||
DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
|
||||
// Sometimes aListener is a main-thread only object like XPCWrappedJS, and
|
||||
// sometimes it's a threadsafe object like nsSOCKSSocketInfo. Use a main-
|
||||
// thread pointer holder, but disable strict enforcement of thread invariants.
|
||||
// The AddRef implementation of XPCWrappedJS will assert if we go wrong here.
|
||||
: mListener(new nsMainThreadPtrHolder<nsIDNSListener>(aListener, false))
|
||||
, mTargetThread(aTargetThread)
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
NS_DECL_NSIDNSLISTENERPROXY
|
||||
|
||||
class OnLookupCompleteRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
OnLookupCompleteRunnable(const nsMainThreadPtrHandle<nsIDNSListener>& aListener,
|
||||
nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
: mListener(aListener)
|
||||
, mRequest(aRequest)
|
||||
, mRecord(aRecord)
|
||||
, mStatus(aStatus)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsICancelable> mRequest;
|
||||
nsCOMPtr<nsIDNSRecord> mRecord;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
private:
|
||||
~DNSListenerProxy() {}
|
||||
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mTargetThread;
|
||||
};
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DNSListenerProxy_h__
|
||||
313
netwerk/dns/DNSRequestChild.cpp
Normal file
313
netwerk/dns/DNSRequestChild.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/net/ChildDNSService.h"
|
||||
#include "mozilla/net/DNSRequestChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsNetAddr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSRecord:
|
||||
// A simple class to provide nsIDNSRecord on the child
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ChildDNSRecord : public nsIDNSRecord
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSRECORD
|
||||
|
||||
ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
|
||||
|
||||
private:
|
||||
virtual ~ChildDNSRecord();
|
||||
|
||||
nsCString mCanonicalName;
|
||||
nsTArray<NetAddr> mAddresses;
|
||||
uint32_t mCurrent; // addr iterator
|
||||
uint32_t mLength; // number of addrs
|
||||
uint16_t mFlags;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord)
|
||||
|
||||
ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
|
||||
: mCurrent(0)
|
||||
, mFlags(flags)
|
||||
{
|
||||
mCanonicalName = reply.canonicalName();
|
||||
|
||||
// A shame IPDL gives us no way to grab ownership of array: so copy it.
|
||||
const nsTArray<NetAddr>& addrs = reply.addrs();
|
||||
uint32_t i = 0;
|
||||
mLength = addrs.Length();
|
||||
for (; i < mLength; i++) {
|
||||
mAddresses.AppendElement(addrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ChildDNSRecord::~ChildDNSRecord()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSRecord::nsIDNSRecord
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetCanonicalName(nsACString &result)
|
||||
{
|
||||
if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
result = mCanonicalName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
|
||||
{
|
||||
if (mCurrent >= mLength) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
|
||||
|
||||
// both Ipv4/6 use same bits for port, so safe to just use ipv4's field
|
||||
addr->inet.port = htons(port);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray)
|
||||
{
|
||||
aAddressArray = mAddresses;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// shamelessly copied from nsDNSRecord
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result)
|
||||
{
|
||||
NetAddr addr;
|
||||
nsresult rv = GetNextAddr(port, &addr);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ADDREF(*result = new nsNetAddr(&addr));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// also copied from nsDNSRecord
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetNextAddrAsString(nsACString &result)
|
||||
{
|
||||
NetAddr addr;
|
||||
nsresult rv = GetNextAddr(0, &addr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
char buf[kIPv6CStrBufSize];
|
||||
if (NetAddrToString(&addr, buf, sizeof(buf))) {
|
||||
result.Assign(buf);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ERROR("NetAddrToString failed unexpectedly");
|
||||
return NS_ERROR_FAILURE; // conversion failed for some reason
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::HasMore(bool *result)
|
||||
{
|
||||
*result = mCurrent < mLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::Rewind()
|
||||
{
|
||||
mCurrent = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::ReportUnusable(uint16_t aPort)
|
||||
{
|
||||
// "We thank you for your feedback" == >/dev/null
|
||||
// TODO: we could send info back to parent.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CancelDNSRequestEvent
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CancelDNSRequestEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
CancelDNSRequestEvent(DNSRequestChild* aDnsReq, nsresult aReason)
|
||||
: mDnsRequest(aDnsReq)
|
||||
, mReasonForCancel(aReason)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (mDnsRequest->mIPCOpen) {
|
||||
// Send request to Parent process.
|
||||
mDnsRequest->SendCancelDNSRequest(mDnsRequest->mHost, mDnsRequest->mFlags,
|
||||
mDnsRequest->mNetworkInterface,
|
||||
mReasonForCancel);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
RefPtr<DNSRequestChild> mDnsRequest;
|
||||
nsresult mReasonForCancel;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DNSRequestChild::DNSRequestChild(const nsCString& aHost,
|
||||
const uint32_t& aFlags,
|
||||
const nsCString& aNetworkInterface,
|
||||
nsIDNSListener *aListener,
|
||||
nsIEventTarget *target)
|
||||
: mListener(aListener)
|
||||
, mTarget(target)
|
||||
, mResultStatus(NS_OK)
|
||||
, mHost(aHost)
|
||||
, mFlags(aFlags)
|
||||
, mNetworkInterface(aNetworkInterface)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::StartRequest()
|
||||
{
|
||||
// we can only do IPDL on the main thread
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod(this, &DNSRequestChild::StartRequest));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send request to Parent process.
|
||||
gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags,
|
||||
mNetworkInterface);
|
||||
mIPCOpen = true;
|
||||
|
||||
// IPDL holds a reference until IPDL channel gets destroyed
|
||||
AddIPDLReference();
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::CallOnLookupComplete()
|
||||
{
|
||||
MOZ_ASSERT(mListener);
|
||||
mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
|
||||
}
|
||||
|
||||
bool
|
||||
DNSRequestChild::RecvLookupCompleted(const DNSRequestResponse& reply)
|
||||
{
|
||||
mIPCOpen = false;
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
switch (reply.type()) {
|
||||
case DNSRequestResponse::TDNSRecord: {
|
||||
mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
|
||||
break;
|
||||
}
|
||||
case DNSRequestResponse::Tnsresult: {
|
||||
mResultStatus = reply.get_nsresult();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("unknown type");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
bool targetIsMain = false;
|
||||
if (!mTarget) {
|
||||
targetIsMain = true;
|
||||
} else {
|
||||
mTarget->IsOnCurrentThread(&targetIsMain);
|
||||
}
|
||||
|
||||
if (targetIsMain) {
|
||||
CallOnLookupComplete();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete);
|
||||
mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
Unused << Send__delete__(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::ReleaseIPDLReference()
|
||||
{
|
||||
// Request is done or destroyed. Remove it from the hash table.
|
||||
RefPtr<ChildDNSService> dnsServiceChild =
|
||||
dont_AddRef(ChildDNSService::GetSingleton());
|
||||
dnsServiceChild->NotifyRequestDone(this);
|
||||
|
||||
Release();
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
mIPCOpen = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(DNSRequestChild,
|
||||
nsICancelable)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild::nsICancelable
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSRequestChild::Cancel(nsresult reason)
|
||||
{
|
||||
if(mIPCOpen) {
|
||||
// We can only do IPDL on the main thread
|
||||
NS_DispatchToMainThread(
|
||||
new CancelDNSRequestEvent(this, reason));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
61
netwerk/dns/DNSRequestChild.h
Normal file
61
netwerk/dns/DNSRequestChild.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#ifndef mozilla_net_DNSRequestChild_h
|
||||
#define mozilla_net_DNSRequestChild_h
|
||||
|
||||
#include "mozilla/net/PDNSRequestChild.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSRequestChild final
|
||||
: public PDNSRequestChild
|
||||
, public nsICancelable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICANCELABLE
|
||||
|
||||
DNSRequestChild(const nsCString& aHost, const uint32_t& aFlags,
|
||||
const nsCString& aNetworkInterface,
|
||||
nsIDNSListener *aListener, nsIEventTarget *target);
|
||||
|
||||
void AddIPDLReference() {
|
||||
AddRef();
|
||||
}
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
// Sends IPDL request to parent
|
||||
void StartRequest();
|
||||
void CallOnLookupComplete();
|
||||
|
||||
protected:
|
||||
friend class CancelDNSRequestEvent;
|
||||
friend class ChildDNSService;
|
||||
virtual ~DNSRequestChild() {}
|
||||
|
||||
virtual bool RecvLookupCompleted(const DNSRequestResponse& reply) override;
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
nsCOMPtr<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
nsCOMPtr<nsIDNSRecord> mResultRecord;
|
||||
nsresult mResultStatus;
|
||||
nsCString mHost;
|
||||
uint16_t mFlags;
|
||||
nsCString mNetworkInterface;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_DNSRequestChild_h
|
||||
132
netwerk/dns/DNSRequestParent.cpp
Normal file
132
netwerk/dns/DNSRequestParent.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/net/DNSRequestParent.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
DNSRequestParent::DNSRequestParent()
|
||||
: mFlags(0)
|
||||
, mIPCClosed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DNSRequestParent::~DNSRequestParent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags,
|
||||
const nsACString &networkInterface)
|
||||
{
|
||||
nsresult rv;
|
||||
mFlags = flags;
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
nsCOMPtr<nsICancelable> unused;
|
||||
rv = dns->AsyncResolveExtended(hostname, flags, networkInterface, this,
|
||||
mainThread, getter_AddRefs(unused));
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) && !mIPCClosed) {
|
||||
mIPCClosed = true;
|
||||
Unused << SendLookupCompleted(DNSRequestResponse(rv));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DNSRequestParent::RecvCancelDNSRequest(const nsCString& hostName,
|
||||
const uint32_t& flags,
|
||||
const nsCString& networkInterface,
|
||||
const nsresult& reason)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = dns->CancelAsyncResolveExtended(hostName, flags, networkInterface,
|
||||
this, reason);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DNSRequestParent::Recv__delete__()
|
||||
{
|
||||
mIPCClosed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// We may still have refcount>0 if DNS hasn't called our OnLookupComplete
|
||||
// yet, but child process has crashed. We must not send any more msgs
|
||||
// to child, or IPDL will kill chrome process, too.
|
||||
mIPCClosed = true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestParent::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(DNSRequestParent,
|
||||
nsIDNSListener)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIDNSListener functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSRequestParent::OnLookupComplete(nsICancelable *request,
|
||||
nsIDNSRecord *rec,
|
||||
nsresult status)
|
||||
{
|
||||
if (mIPCClosed) {
|
||||
// nothing to do: child probably crashed
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
MOZ_ASSERT(rec);
|
||||
|
||||
nsAutoCString cname;
|
||||
if (mFlags & nsHostResolver::RES_CANON_NAME) {
|
||||
rec->GetCanonicalName(cname);
|
||||
}
|
||||
|
||||
// Get IP addresses for hostname (use port 80 as dummy value for NetAddr)
|
||||
NetAddrArray array;
|
||||
NetAddr addr;
|
||||
while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) {
|
||||
array.AppendElement(addr);
|
||||
}
|
||||
|
||||
Unused << SendLookupCompleted(DNSRequestResponse(DNSRecord(cname, array)));
|
||||
} else {
|
||||
Unused << SendLookupCompleted(DNSRequestResponse(status));
|
||||
}
|
||||
|
||||
mIPCClosed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
50
netwerk/dns/DNSRequestParent.h
Normal file
50
netwerk/dns/DNSRequestParent.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#ifndef mozilla_net_DNSRequestParent_h
|
||||
#define mozilla_net_DNSRequestParent_h
|
||||
|
||||
#include "mozilla/net/PDNSRequestParent.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsIDNSListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSRequestParent
|
||||
: public PDNSRequestParent
|
||||
, public nsIDNSListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
DNSRequestParent();
|
||||
|
||||
void DoAsyncResolve(const nsACString &hostname, uint32_t flags,
|
||||
const nsACString &networkInterface);
|
||||
|
||||
// Pass args here rather than storing them in the parent; they are only
|
||||
// needed if the request is to be canceled.
|
||||
bool RecvCancelDNSRequest(const nsCString& hostName,
|
||||
const uint32_t& flags,
|
||||
const nsCString& networkInterface,
|
||||
const nsresult& reason) override;
|
||||
bool Recv__delete__() override;
|
||||
|
||||
protected:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
private:
|
||||
virtual ~DNSRequestParent();
|
||||
|
||||
uint32_t mFlags;
|
||||
bool mIPCClosed; // true if IPDL channel has been closed (child crash)
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_DNSRequestParent_h
|
||||
369
netwerk/dns/GetAddrInfo.cpp
Normal file
369
netwerk/dns/GetAddrInfo.cpp
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/* -*- 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 "GetAddrInfo.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsError.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include <algorithm>
|
||||
#include "prerror.h"
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
// There is a bug in windns.h where the type of parameter ppQueryResultsSet for
|
||||
// DnsQuery_A is dependent on UNICODE being set. It should *always* be
|
||||
// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
|
||||
// we make sure that UNICODE is unset.
|
||||
#undef UNICODE
|
||||
#include <ws2tcpip.h>
|
||||
#undef GetAddrInfo
|
||||
#include <windns.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static LazyLogModule gGetAddrInfoLog("GetAddrInfo");
|
||||
#define LOG(msg, ...) \
|
||||
MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
|
||||
#define LOG_WARNING(msg, ...) \
|
||||
MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
////////////////////////////
|
||||
// WINDOWS IMPLEMENTATION //
|
||||
////////////////////////////
|
||||
|
||||
// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
|
||||
// PR_* constants with this API.
|
||||
static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6
|
||||
&& PR_AF_UNSPEC == AF_UNSPEC, "PR_AF_* must match AF_*");
|
||||
|
||||
// We intentionally leak this mutex. This is because we can run into a
|
||||
// situation where the worker threads are still running until the process
|
||||
// is actually fully shut down, and at any time one of those worker
|
||||
// threads can access gDnsapiInfoLock.
|
||||
static OffTheBooksMutex* gDnsapiInfoLock = nullptr;
|
||||
|
||||
struct DnsapiInfo
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DnsapiInfo);
|
||||
|
||||
HMODULE mLibrary;
|
||||
decltype(&DnsQuery_A) mDnsQueryFunc;
|
||||
decltype(&DnsFree) mDnsFreeFunc;
|
||||
|
||||
private:
|
||||
// This will either be called during shutdown of the GetAddrInfo module, or
|
||||
// when a worker thread is done doing a lookup (ie: within
|
||||
// _GetAddrInfo_Windows). Note that the lock must be held when this is
|
||||
// called.
|
||||
~DnsapiInfo()
|
||||
{
|
||||
if (gDnsapiInfoLock) {
|
||||
gDnsapiInfoLock->AssertCurrentThreadOwns();
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("No mutex available during GetAddrInfo "
|
||||
"shutdown.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("Freeing Dnsapi.dll");
|
||||
MOZ_ASSERT(mLibrary);
|
||||
DebugOnly<BOOL> rv = FreeLibrary(mLibrary);
|
||||
NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
|
||||
}
|
||||
};
|
||||
|
||||
static StaticRefPtr<DnsapiInfo> gDnsapiInfo;
|
||||
|
||||
static MOZ_ALWAYS_INLINE nsresult
|
||||
_GetAddrInfoInit_Windows()
|
||||
{
|
||||
// This is necessary to ensure strict thread safety because if two threads
|
||||
// run this function at the same time they can potentially create two
|
||||
// mutexes.
|
||||
MOZ_ASSERT(NS_IsMainThread(),
|
||||
"Do not initialize GetAddrInfo off main thread!");
|
||||
|
||||
if (!gDnsapiInfoLock) {
|
||||
gDnsapiInfoLock = new OffTheBooksMutex("GetAddrInfo.cpp::gDnsapiInfoLock");
|
||||
}
|
||||
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
|
||||
|
||||
if (gDnsapiInfo) {
|
||||
MOZ_ASSERT_UNREACHABLE("GetAddrInfo is being initialized multiple times!");
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
HMODULE library = LoadLibraryA("Dnsapi.dll");
|
||||
if (NS_WARN_IF(!library)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FARPROC dnsQueryFunc = GetProcAddress(library, "DnsQuery_A");
|
||||
FARPROC dnsFreeFunc = GetProcAddress(library, "DnsFree");
|
||||
if (NS_WARN_IF(!dnsQueryFunc) || NS_WARN_IF(!dnsFreeFunc)) {
|
||||
DebugOnly<BOOL> rv = FreeLibrary(library);
|
||||
NS_WARNING_ASSERTION(rv, "Failed to free Dnsapi.dll.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DnsapiInfo* info = new DnsapiInfo;
|
||||
info->mLibrary = library;
|
||||
info->mDnsQueryFunc = (decltype(info->mDnsQueryFunc)) dnsQueryFunc;
|
||||
info->mDnsFreeFunc = (decltype(info->mDnsFreeFunc)) dnsFreeFunc;
|
||||
gDnsapiInfo = info;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE nsresult
|
||||
_GetAddrInfoShutdown_Windows()
|
||||
{
|
||||
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
|
||||
|
||||
if (NS_WARN_IF(!gDnsapiInfo) || NS_WARN_IF(!gDnsapiInfoLock)) {
|
||||
MOZ_ASSERT_UNREACHABLE("GetAddrInfo not initialized!");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
gDnsapiInfo = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If successful, returns in aResult a TTL value that is smaller or
|
||||
// equal with the one already there. Gets the TTL value by calling
|
||||
// to dnsapi->mDnsQueryFunc and iterating through the returned
|
||||
// records to find the one with the smallest TTL value.
|
||||
static MOZ_ALWAYS_INLINE nsresult
|
||||
_GetMinTTLForRequestType_Windows(DnsapiInfo * dnsapi, const char* aHost,
|
||||
uint16_t aRequestType, unsigned int* aResult)
|
||||
{
|
||||
MOZ_ASSERT(dnsapi);
|
||||
MOZ_ASSERT(aHost);
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
PDNS_RECORDA dnsData = nullptr;
|
||||
DNS_STATUS status = dnsapi->mDnsQueryFunc(
|
||||
aHost,
|
||||
aRequestType,
|
||||
(DNS_QUERY_STANDARD | DNS_QUERY_NO_NETBT | DNS_QUERY_NO_HOSTS_FILE
|
||||
| DNS_QUERY_NO_MULTICAST | DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE
|
||||
| DNS_QUERY_DONT_RESET_TTL_VALUES),
|
||||
nullptr,
|
||||
&dnsData,
|
||||
nullptr);
|
||||
if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR
|
||||
|| !dnsData) {
|
||||
LOG("No DNS records found for %s. status=%X. aRequestType = %X\n",
|
||||
aHost, status, aRequestType);
|
||||
return NS_ERROR_FAILURE;
|
||||
} else if (status != NOERROR) {
|
||||
LOG_WARNING("DnsQuery_A failed with status %X.\n", status);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
for (PDNS_RECORDA curRecord = dnsData; curRecord; curRecord = curRecord->pNext) {
|
||||
// Only records in the answer section are important
|
||||
if (curRecord->Flags.S.Section != DnsSectionAnswer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curRecord->wType == aRequestType) {
|
||||
*aResult = std::min<unsigned int>(*aResult, curRecord->dwTtl);
|
||||
} else {
|
||||
LOG("Received unexpected record type %u in response for %s.\n",
|
||||
curRecord->wType, aHost);
|
||||
}
|
||||
}
|
||||
|
||||
dnsapi->mDnsFreeFunc(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE nsresult
|
||||
_GetTTLData_Windows(const char* aHost, uint16_t* aResult, uint16_t aAddressFamily)
|
||||
{
|
||||
MOZ_ASSERT(aHost);
|
||||
MOZ_ASSERT(aResult);
|
||||
if (aAddressFamily != PR_AF_UNSPEC &&
|
||||
aAddressFamily != PR_AF_INET &&
|
||||
aAddressFamily != PR_AF_INET6) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
RefPtr<DnsapiInfo> dnsapi = nullptr;
|
||||
{
|
||||
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
|
||||
dnsapi = gDnsapiInfo;
|
||||
}
|
||||
|
||||
if (!dnsapi) {
|
||||
LOG_WARNING("GetAddrInfo has been shutdown or has not been initialized.");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// In order to avoid using ANY records which are not always implemented as a
|
||||
// "Gimme what you have" request in hostname resolvers, we should send A
|
||||
// and/or AAAA requests, based on the address family requested.
|
||||
unsigned int ttl = -1;
|
||||
if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET) {
|
||||
_GetMinTTLForRequestType_Windows(dnsapi, aHost, DNS_TYPE_A, &ttl);
|
||||
}
|
||||
if (aAddressFamily == PR_AF_UNSPEC || aAddressFamily == PR_AF_INET6) {
|
||||
_GetMinTTLForRequestType_Windows(dnsapi, aHost, DNS_TYPE_AAAA, &ttl);
|
||||
}
|
||||
|
||||
{
|
||||
// dnsapi's destructor is not thread-safe, so we release explicitly here
|
||||
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
|
||||
dnsapi = nullptr;
|
||||
}
|
||||
|
||||
if (ttl == -1) {
|
||||
LOG("No useable TTL found.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*aResult = ttl;
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////
|
||||
// PORTABLE RUNTIME IMPLEMENTATION//
|
||||
////////////////////////////////////
|
||||
|
||||
static MOZ_ALWAYS_INLINE nsresult
|
||||
_GetAddrInfo_Portable(const char* aCanonHost, uint16_t aAddressFamily,
|
||||
uint16_t aFlags, const char* aNetworkInterface,
|
||||
AddrInfo** aAddrInfo)
|
||||
{
|
||||
MOZ_ASSERT(aCanonHost);
|
||||
MOZ_ASSERT(aAddrInfo);
|
||||
|
||||
// We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
|
||||
// need to translate the aFlags into a form that PR_GetAddrInfoByName
|
||||
// accepts.
|
||||
int prFlags = PR_AI_ADDRCONFIG;
|
||||
if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
|
||||
prFlags |= PR_AI_NOCANONNAME;
|
||||
}
|
||||
|
||||
// We need to remove IPv4 records manually because PR_GetAddrInfoByName
|
||||
// doesn't support PR_AF_INET6.
|
||||
bool disableIPv4 = aAddressFamily == PR_AF_INET6;
|
||||
if (disableIPv4) {
|
||||
aAddressFamily = PR_AF_UNSPEC;
|
||||
}
|
||||
|
||||
PRAddrInfo* prai = PR_GetAddrInfoByName(aCanonHost, aAddressFamily, prFlags);
|
||||
|
||||
if (!prai) {
|
||||
return NS_ERROR_UNKNOWN_HOST;
|
||||
}
|
||||
|
||||
const char* canonName = nullptr;
|
||||
if (aFlags & nsHostResolver::RES_CANON_NAME) {
|
||||
canonName = PR_GetCanonNameFromAddrInfo(prai);
|
||||
}
|
||||
|
||||
bool filterNameCollision = !(aFlags & nsHostResolver::RES_ALLOW_NAME_COLLISION);
|
||||
nsAutoPtr<AddrInfo> ai(new AddrInfo(aCanonHost, prai, disableIPv4,
|
||||
filterNameCollision, canonName));
|
||||
PR_FreeAddrInfo(prai);
|
||||
if (ai->mAddresses.isEmpty()) {
|
||||
return NS_ERROR_UNKNOWN_HOST;
|
||||
}
|
||||
|
||||
*aAddrInfo = ai.forget();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// COMMON/PLATFORM INDEPENDENT CODE //
|
||||
//////////////////////////////////////
|
||||
nsresult
|
||||
GetAddrInfoInit() {
|
||||
LOG("Initializing GetAddrInfo.\n");
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
return _GetAddrInfoInit_Windows();
|
||||
#else
|
||||
return NS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetAddrInfoShutdown() {
|
||||
LOG("Shutting down GetAddrInfo.\n");
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
return _GetAddrInfoShutdown_Windows();
|
||||
#else
|
||||
return NS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
|
||||
const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl)
|
||||
{
|
||||
if (NS_WARN_IF(!aHost) || NS_WARN_IF(!aAddrInfo)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
// The GetTTLData needs the canonical name to function properly
|
||||
if (aGetTtl) {
|
||||
aFlags |= nsHostResolver::RES_CANON_NAME;
|
||||
}
|
||||
#endif
|
||||
|
||||
*aAddrInfo = nullptr;
|
||||
nsresult rv = _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags,
|
||||
aNetworkInterface, aAddrInfo);
|
||||
|
||||
#if DNSQUERY_AVAILABLE
|
||||
if (aGetTtl && NS_SUCCEEDED(rv)) {
|
||||
// Figure out the canonical name, or if that fails, just use the host name
|
||||
// we have.
|
||||
const char *name = nullptr;
|
||||
if (*aAddrInfo != nullptr && (*aAddrInfo)->mCanonicalName) {
|
||||
name = (*aAddrInfo)->mCanonicalName;
|
||||
} else {
|
||||
name = aHost;
|
||||
}
|
||||
|
||||
LOG("Getting TTL for %s (cname = %s).", aHost, name);
|
||||
uint16_t ttl = 0;
|
||||
nsresult ttlRv = _GetTTLData_Windows(name, &ttl, aAddressFamily);
|
||||
if (NS_SUCCEEDED(ttlRv)) {
|
||||
(*aAddrInfo)->ttl = ttl;
|
||||
LOG("Got TTL %u for %s (name = %s).", ttl, aHost, name);
|
||||
} else {
|
||||
LOG_WARNING("Could not get TTL for %s (cname = %s).", aHost, name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
67
netwerk/dns/GetAddrInfo.h
Normal file
67
netwerk/dns/GetAddrInfo.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef netwerk_dns_GetAddrInfo_h
|
||||
#define netwerk_dns_GetAddrInfo_h
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nscore.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#define DNSQUERY_AVAILABLE 1
|
||||
#define TTL_AVAILABLE 1
|
||||
#else
|
||||
#define DNSQUERY_AVAILABLE 0
|
||||
#define TTL_AVAILABLE 0
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class AddrInfo;
|
||||
|
||||
/**
|
||||
* Look up a host by name. Mostly equivalent to getaddrinfo(host, NULL, ...) of
|
||||
* RFC 3493.
|
||||
*
|
||||
* @param aHost[in] Character string defining the host name of interest
|
||||
* @param aAddressFamily[in] May be AF_INET, AF_INET6, or AF_UNSPEC.
|
||||
* @param aFlags[in] May be either PR_AI_ADDRCONFIG or
|
||||
* PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include PR_AI_NOCANONNAME to
|
||||
* suppress the determination of the canonical name corresponding to
|
||||
* hostname (PR_AI_NOCANONNAME will be ignored if the TTL is retrieved).
|
||||
* @param aAddrInfo[out] Will point to the results of the host lookup, or be
|
||||
* null if the lookup failed.
|
||||
* @param aGetTtl[in] If true, and TTL_AVAILABLE is truthy, the TTL will be
|
||||
* retrieved if DNS provides the answers..
|
||||
*/
|
||||
nsresult
|
||||
GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
|
||||
const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl);
|
||||
|
||||
/**
|
||||
* Initialize the GetAddrInfo module.
|
||||
*
|
||||
* GetAddrInfoShutdown() should be called for every time this function is
|
||||
* called.
|
||||
*/
|
||||
nsresult
|
||||
GetAddrInfoInit();
|
||||
|
||||
/**
|
||||
* Shutdown the GetAddrInfo module.
|
||||
*
|
||||
* This function should be called for every time GetAddrInfoInit() is called.
|
||||
* An assertion may throw (but is not guarenteed) if this function is called
|
||||
* too many times.
|
||||
*/
|
||||
nsresult
|
||||
GetAddrInfoShutdown();
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // netwerk_dns_GetAddrInfo_h
|
||||
23
netwerk/dns/PDNSParams.h
Normal file
23
netwerk/dns/PDNSParams.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=c: */
|
||||
|
||||
/* 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 PDNSParams_h
|
||||
#define PDNSParams_h
|
||||
|
||||
#include "DNS.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Need to define typedef in .h file--can't seem to in ipdl.h file?
|
||||
typedef nsTArray<NetAddr> NetAddrArray;
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // PDNSParams_h
|
||||
36
netwerk/dns/PDNSRequest.ipdl
Normal file
36
netwerk/dns/PDNSRequest.ipdl
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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 protocol PNecko;
|
||||
|
||||
include PDNSRequestParams;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
async protocol PDNSRequest
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
parent:
|
||||
// constructor in PNecko takes AsyncResolve args that initialize request
|
||||
|
||||
// Pass args here rather than storing them in the parent; they are only
|
||||
// needed if the request is to be canceled.
|
||||
async CancelDNSRequest(nsCString hostName, uint32_t flags,
|
||||
nsCString networkInterface, nsresult reason);
|
||||
async __delete__();
|
||||
|
||||
child:
|
||||
async LookupCompleted(DNSRequestResponse reply);
|
||||
|
||||
};
|
||||
|
||||
} //namespace net
|
||||
} //namespace mozilla
|
||||
31
netwerk/dns/PDNSRequestParams.ipdlh
Normal file
31
netwerk/dns/PDNSRequestParams.ipdlh
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
using NetAddrArray from "mozilla/net/PDNSParams.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNS IPDL structs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct DNSRecord
|
||||
{
|
||||
nsCString canonicalName;
|
||||
NetAddrArray addrs;
|
||||
};
|
||||
|
||||
union DNSRequestResponse
|
||||
{
|
||||
DNSRecord;
|
||||
nsresult; // if error
|
||||
};
|
||||
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
10834
netwerk/dns/effective_tld_names.dat
Normal file
10834
netwerk/dns/effective_tld_names.dat
Normal file
File diff suppressed because it is too large
Load diff
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;
|
||||
13
netwerk/dns/mdns/moz.build
Normal file
13
netwerk/dns/mdns/moz.build
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# -*- 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/.
|
||||
|
||||
DIRS += ['libmdns']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDNSServiceDiscovery.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_mdns'
|
||||
219
netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
Normal file
219
netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsICancelable;
|
||||
interface nsIPropertyBag2;
|
||||
|
||||
/**
|
||||
* Service information
|
||||
*/
|
||||
[scriptable, uuid(670ed0f9-2fa5-4544-bf1e-ea58ac179374)]
|
||||
interface nsIDNSServiceInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* The host name of the service. (E.g. "Android.local.")
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute AUTF8String host;
|
||||
|
||||
/**
|
||||
* The IP address of the service.
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute AUTF8String address;
|
||||
|
||||
/**
|
||||
* The port number of the service. (E.g. 80)
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute unsigned short port;
|
||||
|
||||
/**
|
||||
* The service name of the service for display. (E.g. "My TV")
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute AUTF8String serviceName;
|
||||
|
||||
/**
|
||||
* The type of the service. (E.g. "_http._tcp")
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute AUTF8String serviceType;
|
||||
|
||||
/**
|
||||
* The domain name of the service. (E.g. "local.")
|
||||
* @throws NS_ERROR_NOT_INITIALIZED when getting unset value.
|
||||
*/
|
||||
attribute AUTF8String domainName;
|
||||
|
||||
/**
|
||||
* The attributes of the service.
|
||||
*/
|
||||
attribute nsIPropertyBag2 attributes;
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback interface for service discovery
|
||||
*/
|
||||
[scriptable, uuid(3025b7f2-97bb-435b-b43d-26731b3f5fc4)]
|
||||
interface nsIDNSServiceDiscoveryListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback when the discovery begins.
|
||||
* @param aServiceType
|
||||
* the service type of |startDiscovery|.
|
||||
*/
|
||||
void onDiscoveryStarted(in AUTF8String aServiceType);
|
||||
|
||||
/**
|
||||
* Callback when the discovery ends.
|
||||
* @param aServiceType
|
||||
* the service type of |startDiscovery|.
|
||||
*/
|
||||
void onDiscoveryStopped(in AUTF8String aServiceType);
|
||||
|
||||
/**
|
||||
* Callback when the a service is found.
|
||||
* @param aServiceInfo
|
||||
* the info about the found service, where |serviceName|, |aServiceType|, and |domainName| are set.
|
||||
*/
|
||||
void onServiceFound(in nsIDNSServiceInfo aServiceInfo);
|
||||
|
||||
/**
|
||||
* Callback when the a service is lost.
|
||||
* @param aServiceInfo
|
||||
* the info about the lost service, where |serviceName|, |aServiceType|, and |domainName| are set.
|
||||
*/
|
||||
void onServiceLost(in nsIDNSServiceInfo aServiceInfo);
|
||||
|
||||
/**
|
||||
* Callback when the discovery cannot start.
|
||||
* @param aServiceType
|
||||
* the service type of |startDiscovery|.
|
||||
* @param aErrorCode
|
||||
* the error code.
|
||||
*/
|
||||
void onStartDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode);
|
||||
|
||||
/**
|
||||
* Callback when the discovery cannot stop.
|
||||
* @param aServiceType
|
||||
* the service type of |startDiscovery|.
|
||||
* @param aErrorCode
|
||||
* the error code.
|
||||
*/
|
||||
void onStopDiscoveryFailed(in AUTF8String aServiceType, in long aErrorCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback interface for service registration
|
||||
*/
|
||||
[scriptable, uuid(e165e4be-abf4-4963-a66d-ed3ca116e5e4)]
|
||||
interface nsIDNSRegistrationListener : nsISupports
|
||||
{
|
||||
const long ERROR_SERVICE_NOT_RUNNING = -65563;
|
||||
|
||||
/**
|
||||
* Callback when the service is registered successfully.
|
||||
* @param aServiceInfo
|
||||
* the info about the registered service,
|
||||
* where |serviceName|, |aServiceType|, and |domainName| are set.
|
||||
*/
|
||||
void onServiceRegistered(in nsIDNSServiceInfo aServiceInfo);
|
||||
|
||||
/**
|
||||
* Callback when the service is unregistered successfully.
|
||||
* @param aServiceInfo
|
||||
* the info about the unregistered service.
|
||||
*/
|
||||
void onServiceUnregistered(in nsIDNSServiceInfo aServiceInfo);
|
||||
|
||||
/**
|
||||
* Callback when the service cannot be registered.
|
||||
* @param aServiceInfo
|
||||
* the info about the service to be registered.
|
||||
* @param aErrorCode
|
||||
* the error code.
|
||||
*/
|
||||
void onRegistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
|
||||
|
||||
/**
|
||||
* Callback when the service cannot be unregistered.
|
||||
* @param aServiceInfo
|
||||
* the info about the registered service.
|
||||
* @param aErrorCode
|
||||
* the error code.
|
||||
*/
|
||||
void onUnregistrationFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback interface for service resolve
|
||||
*/
|
||||
[scriptable, uuid(24ee6408-648e-421d-accf-c6e5adeccf97)]
|
||||
interface nsIDNSServiceResolveListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback when the service is resolved successfully.
|
||||
* @param aServiceInfo
|
||||
* the info about the resolved service, where |host| and |port| are set.
|
||||
*/
|
||||
void onServiceResolved(in nsIDNSServiceInfo aServiceInfo);
|
||||
|
||||
/**
|
||||
* Callback when the service cannot be resolved.
|
||||
* @param aServiceInfo
|
||||
* the info about the service to be resolved.
|
||||
* @param aErrorCode
|
||||
* the error code.
|
||||
*/
|
||||
void onResolveFailed(in nsIDNSServiceInfo aServiceInfo, in long aErrorCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* The interface for DNS service discovery/registration/resolve
|
||||
*/
|
||||
[scriptable, uuid(6487899b-beb1-455a-ba65-e4fd465066d7)]
|
||||
interface nsIDNSServiceDiscovery : nsISupports
|
||||
{
|
||||
/**
|
||||
* Browse for instances of a service.
|
||||
* @param aServiceType
|
||||
* the service type to be discovered, E.g. "_http._tcp".
|
||||
* @param aListener
|
||||
* callback interface for discovery notifications.
|
||||
* @return An object that can be used to cancel the service discovery.
|
||||
*/
|
||||
nsICancelable startDiscovery(in AUTF8String aServiceType, in nsIDNSServiceDiscoveryListener aListener);
|
||||
|
||||
/**
|
||||
* Register a service that is discovered via |startDiscovery| and |resolveService| calls.
|
||||
* @param aServiceInfo
|
||||
* the service information to be registered.
|
||||
* |port| and |aServiceType| are required attributes.
|
||||
* @param aListener
|
||||
* callback interface for registration notifications.
|
||||
* @return An object that can be used to cancel the service registration.
|
||||
*/
|
||||
nsICancelable registerService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSRegistrationListener aListener);
|
||||
|
||||
/**
|
||||
* Resolve a service name discovered via |startDiscovery| to a target host name, port number.
|
||||
* @param aServiceInfo
|
||||
* the service information to be registered.
|
||||
* |serviceName|, |aServiceType|, and |domainName| are required attributes as reported to the |onServiceFound| callback.
|
||||
* @param aListener
|
||||
* callback interface for registration notifications.
|
||||
*/
|
||||
void resolveService(in nsIDNSServiceInfo aServiceInfo, in nsIDNSServiceResolveListener aListener);
|
||||
};
|
||||
|
||||
%{ C++
|
||||
#define DNSSERVICEDISCOVERY_CONTRACT_ID \
|
||||
"@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1"
|
||||
#define DNSSERVICEINFO_CONTRACT_ID \
|
||||
"@mozilla.org/toolkit/components/mdnsresponder/dns-info;1"
|
||||
%}
|
||||
78
netwerk/dns/moz.build
Normal file
78
netwerk/dns/moz.build
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# -*- 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/.
|
||||
|
||||
DIRS += [
|
||||
'mdns',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDNSListener.idl',
|
||||
'nsIDNSRecord.idl',
|
||||
'nsIDNSService.idl',
|
||||
'nsIEffectiveTLDService.idl',
|
||||
'nsIIDNService.idl',
|
||||
'nsPIDNSService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_dns'
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'ChildDNSService.h',
|
||||
'DNS.h',
|
||||
'DNSListenerProxy.h',
|
||||
'DNSRequestChild.h',
|
||||
'DNSRequestParent.h',
|
||||
'PDNSParams.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'nsEffectiveTLDService.cpp', # Excluded from UNIFIED_SOURCES due to special build flags.
|
||||
'nsHostResolver.cpp', # Redefines LOG
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChildDNSService.cpp',
|
||||
'DNS.cpp',
|
||||
'DNSListenerProxy.cpp',
|
||||
'DNSRequestChild.cpp',
|
||||
'DNSRequestParent.cpp',
|
||||
'GetAddrInfo.cpp',
|
||||
'nsDNSService2.cpp',
|
||||
'nsIDNService.cpp',
|
||||
'punycode.c',
|
||||
]
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'PDNSRequest.ipdl',
|
||||
'PDNSRequestParams.ipdlh',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
GENERATED_FILES = [
|
||||
'etld_data.inc',
|
||||
]
|
||||
etld_data = GENERATED_FILES['etld_data.inc']
|
||||
etld_data.script = 'prepare_tlds.py'
|
||||
etld_data.inputs = ['effective_tld_names.dat']
|
||||
|
||||
# need to include etld_data.inc
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
DEFINES['IDNA2008'] = True
|
||||
USE_LIBS += ['icu']
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
'nameprep.c',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
347
netwerk/dns/nameprep.c
Normal file
347
netwerk/dns/nameprep.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* Copyright (c) 2001,2002 Japan Network Information Center.
|
||||
* All rights reserved.
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set forth bellow.
|
||||
*
|
||||
* LICENSE TERMS AND CONDITIONS
|
||||
*
|
||||
* The following License Terms and Conditions apply, unless a different
|
||||
* license is obtained from Japan Network Information Center ("JPNIC"),
|
||||
* a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
|
||||
* Chiyoda-ku, Tokyo 101-0047, Japan.
|
||||
*
|
||||
* 1. Use, Modification and Redistribution (including distribution of any
|
||||
* modified or derived work) in source and/or binary forms is permitted
|
||||
* under this License Terms and Conditions.
|
||||
*
|
||||
* 2. Redistribution of source code must retain the copyright notices as they
|
||||
* appear in each source code file, this License Terms and Conditions.
|
||||
*
|
||||
* 3. Redistribution in binary form must reproduce the Copyright Notice,
|
||||
* this License Terms and Conditions, in the documentation and/or other
|
||||
* materials provided with the distribution. For the purposes of binary
|
||||
* distribution the "Copyright Notice" refers to the following language:
|
||||
* "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
|
||||
*
|
||||
* 4. The name of JPNIC may not be used to endorse or promote products
|
||||
* derived from this Software without specific prior written approval of
|
||||
* JPNIC.
|
||||
*
|
||||
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nsIDNKitInterface.h"
|
||||
|
||||
#define UCS_MAX 0x7fffffff
|
||||
#define UNICODE_MAX 0x10ffff
|
||||
|
||||
|
||||
/*
|
||||
* Load NAMEPREP compiled tables.
|
||||
*/
|
||||
#include "nameprepdata.c"
|
||||
|
||||
/*
|
||||
* Define mapping/checking functions for each version of the draft.
|
||||
*/
|
||||
|
||||
#define VERSION id11
|
||||
#include "nameprep_template.c"
|
||||
#undef VERSION
|
||||
|
||||
typedef const char *(*nameprep_mapproc)(uint32_t v);
|
||||
typedef int (*nameprep_checkproc)(uint32_t v);
|
||||
typedef idn_biditype_t (*nameprep_biditypeproc)(uint32_t v);
|
||||
|
||||
static struct idn_nameprep {
|
||||
char *version;
|
||||
nameprep_mapproc map_proc;
|
||||
nameprep_checkproc prohibited_proc;
|
||||
nameprep_checkproc unassigned_proc;
|
||||
nameprep_biditypeproc biditype_proc;
|
||||
} nameprep_versions[] = {
|
||||
#define MAKE_NAMEPREP_HANDLE(version, id) \
|
||||
{ version, \
|
||||
compose_sym2(nameprep_map_, id), \
|
||||
compose_sym2(nameprep_prohibited_, id), \
|
||||
compose_sym2(nameprep_unassigned_, id), \
|
||||
compose_sym2(nameprep_biditype_, id), }
|
||||
MAKE_NAMEPREP_HANDLE("nameprep-11", id11),
|
||||
{ NULL, NULL, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static idn_result_t idn_nameprep_check(nameprep_checkproc proc,
|
||||
const uint32_t *str,
|
||||
const uint32_t **found);
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_create(const char *version, idn_nameprep_t *handlep) {
|
||||
idn_nameprep_t handle;
|
||||
|
||||
assert(handlep != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_create(version=%-.50s)\n",
|
||||
version == NULL ? "<NULL>" : version));
|
||||
|
||||
if (version == NULL)
|
||||
version = IDN_NAMEPREP_CURRENT;
|
||||
|
||||
/*
|
||||
* Lookup table for the specified version. Since the number of
|
||||
* versions won't be large (I don't want see draft-23 or such :-),
|
||||
* simple linear search is OK.
|
||||
*/
|
||||
for (handle = nameprep_versions; handle->version != NULL; handle++) {
|
||||
if (strcmp(handle->version, version) == 0) {
|
||||
*handlep = handle;
|
||||
return (idn_success);
|
||||
}
|
||||
}
|
||||
return (idn_notfound);
|
||||
}
|
||||
|
||||
void
|
||||
idn_nameprep_destroy(idn_nameprep_t handle) {
|
||||
assert(handle != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_destroy()\n"));
|
||||
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_map(idn_nameprep_t handle, const uint32_t *from,
|
||||
uint32_t *to, size_t tolen) {
|
||||
assert(handle != NULL && from != NULL && to != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_map(ctx=%s, from=\"%s\")\n",
|
||||
handle->version, idn__debug_ucs4xstring(from, 50)));
|
||||
|
||||
while (*from != '\0') {
|
||||
uint32_t v = *from;
|
||||
const char *mapped;
|
||||
|
||||
if (v > UCS_MAX) {
|
||||
/* This cannot happen, but just in case.. */
|
||||
return (idn_invalid_codepoint);
|
||||
} else if (v > UNICODE_MAX) {
|
||||
/* No mapping is possible. */
|
||||
mapped = NULL;
|
||||
} else {
|
||||
/* Try mapping. */
|
||||
mapped = (*handle->map_proc)(v);
|
||||
}
|
||||
|
||||
if (mapped == NULL) {
|
||||
/* No mapping. Just copy verbatim. */
|
||||
if (tolen < 1)
|
||||
return (idn_buffer_overflow);
|
||||
*to++ = v;
|
||||
tolen--;
|
||||
} else {
|
||||
const unsigned char *mappeddata;
|
||||
size_t mappedlen;
|
||||
|
||||
mappeddata = (const unsigned char *)mapped + 1;
|
||||
mappedlen = *mapped;
|
||||
|
||||
if (tolen < (mappedlen + 3) / 4)
|
||||
return (idn_buffer_overflow);
|
||||
tolen -= (mappedlen + 3) / 4;
|
||||
while (mappedlen >= 4) {
|
||||
*to = *mappeddata++;
|
||||
*to |= *mappeddata++ << 8;
|
||||
*to |= *mappeddata++ << 16;
|
||||
*to |= *mappeddata++ << 24;
|
||||
mappedlen -= 4;
|
||||
to++;
|
||||
}
|
||||
if (mappedlen > 0) {
|
||||
*to = *mappeddata++;
|
||||
*to |= (mappedlen >= 2) ?
|
||||
*mappeddata++ << 8: 0;
|
||||
*to |= (mappedlen >= 3) ?
|
||||
*mappeddata++ << 16: 0;
|
||||
to++;
|
||||
}
|
||||
}
|
||||
from++;
|
||||
}
|
||||
if (tolen == 0)
|
||||
return (idn_buffer_overflow);
|
||||
*to = '\0';
|
||||
return (idn_success);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_isprohibited(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
assert(handle != NULL && str != NULL && found != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_isprohibited(ctx=%s, str=\"%s\")\n",
|
||||
handle->version, idn__debug_ucs4xstring(str, 50)));
|
||||
|
||||
return (idn_nameprep_check(handle->prohibited_proc, str, found));
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_isunassigned(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
assert(handle != NULL && str != NULL && found != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_isunassigned(handle->version, str=\"%s\")\n",
|
||||
handle->version, idn__debug_ucs4xstring(str, 50)));
|
||||
|
||||
return (idn_nameprep_check(handle->unassigned_proc, str, found));
|
||||
}
|
||||
|
||||
static idn_result_t
|
||||
idn_nameprep_check(nameprep_checkproc proc, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
uint32_t v;
|
||||
|
||||
while (*str != '\0') {
|
||||
v = *str;
|
||||
|
||||
if (v > UCS_MAX) {
|
||||
/* This cannot happen, but just in case.. */
|
||||
return (idn_invalid_codepoint);
|
||||
} else if (v > UNICODE_MAX) {
|
||||
/* It is invalid.. */
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
} else if ((*proc)(v)) {
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
}
|
||||
str++;
|
||||
}
|
||||
*found = NULL;
|
||||
return (idn_success);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_isvalidbidi(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
uint32_t v;
|
||||
idn_biditype_t first_char;
|
||||
idn_biditype_t last_char;
|
||||
int found_r_al;
|
||||
|
||||
assert(handle != NULL && str != NULL && found != NULL);
|
||||
|
||||
TRACE(("idn_nameprep_isvalidbidi(ctx=%s, str=\"%s\")\n",
|
||||
handle->version, idn__debug_ucs4xstring(str, 50)));
|
||||
|
||||
if (*str == '\0') {
|
||||
*found = NULL;
|
||||
return (idn_success);
|
||||
}
|
||||
|
||||
/*
|
||||
* check first character's type and initialize variables.
|
||||
*/
|
||||
found_r_al = 0;
|
||||
if (*str > UCS_MAX) {
|
||||
/* This cannot happen, but just in case.. */
|
||||
return (idn_invalid_codepoint);
|
||||
} else if (*str > UNICODE_MAX) {
|
||||
/* It is invalid.. */
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
}
|
||||
first_char = last_char = (*(handle->biditype_proc))(*str);
|
||||
if (first_char == idn_biditype_r_al) {
|
||||
found_r_al = 1;
|
||||
}
|
||||
str++;
|
||||
|
||||
/*
|
||||
* see whether string is valid or not.
|
||||
*/
|
||||
while (*str != '\0') {
|
||||
v = *str;
|
||||
|
||||
if (v > UCS_MAX) {
|
||||
/* This cannot happen, but just in case.. */
|
||||
return (idn_invalid_codepoint);
|
||||
} else if (v > UNICODE_MAX) {
|
||||
/* It is invalid.. */
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
} else {
|
||||
last_char = (*(handle->biditype_proc))(v);
|
||||
if (found_r_al && last_char == idn_biditype_l) {
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
}
|
||||
if (first_char != idn_biditype_r_al && last_char == idn_biditype_r_al) {
|
||||
*found = str;
|
||||
return (idn_success);
|
||||
}
|
||||
if (last_char == idn_biditype_r_al) {
|
||||
found_r_al = 1;
|
||||
}
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
if (found_r_al) {
|
||||
if (last_char != idn_biditype_r_al) {
|
||||
*found = str - 1;
|
||||
return (idn_success);
|
||||
}
|
||||
}
|
||||
|
||||
*found = NULL;
|
||||
return (idn_success);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_createproc(const char *parameter, void **handlep) {
|
||||
return idn_nameprep_create(parameter, (idn_nameprep_t *)handlep);
|
||||
}
|
||||
|
||||
void
|
||||
idn_nameprep_destroyproc(void *handle) {
|
||||
idn_nameprep_destroy((idn_nameprep_t)handle);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_mapproc(void *handle, const uint32_t *from,
|
||||
uint32_t *to, size_t tolen) {
|
||||
return idn_nameprep_map((idn_nameprep_t)handle, from, to, tolen);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_prohibitproc(void *handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
return idn_nameprep_isprohibited((idn_nameprep_t)handle, str, found);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_unassignedproc(void *handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
return idn_nameprep_isunassigned((idn_nameprep_t)handle, str, found);
|
||||
}
|
||||
|
||||
idn_result_t
|
||||
idn_nameprep_bidiproc(void *handle, const uint32_t *str,
|
||||
const uint32_t **found) {
|
||||
return idn_nameprep_isvalidbidi((idn_nameprep_t)handle, str, found);
|
||||
}
|
||||
139
netwerk/dns/nameprep_template.c
Normal file
139
netwerk/dns/nameprep_template.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2001 Japan Network Information Center. All rights reserved.
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set forth bellow.
|
||||
*
|
||||
* LICENSE TERMS AND CONDITIONS
|
||||
*
|
||||
* The following License Terms and Conditions apply, unless a different
|
||||
* license is obtained from Japan Network Information Center ("JPNIC"),
|
||||
* a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
|
||||
* Chiyoda-ku, Tokyo 101-0047, Japan.
|
||||
*
|
||||
* 1. Use, Modification and Redistribution (including distribution of any
|
||||
* modified or derived work) in source and/or binary forms is permitted
|
||||
* under this License Terms and Conditions.
|
||||
*
|
||||
* 2. Redistribution of source code must retain the copyright notices as they
|
||||
* appear in each source code file, this License Terms and Conditions.
|
||||
*
|
||||
* 3. Redistribution in binary form must reproduce the Copyright Notice,
|
||||
* this License Terms and Conditions, in the documentation and/or other
|
||||
* materials provided with the distribution. For the purposes of binary
|
||||
* distribution the "Copyright Notice" refers to the following language:
|
||||
* "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
|
||||
*
|
||||
* 4. The name of JPNIC may not be used to endorse or promote products
|
||||
* derived from this Software without specific prior written approval of
|
||||
* JPNIC.
|
||||
*
|
||||
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Include this file once for each version of NAMEPREP.
|
||||
* VERSION should be defined to appropriate value before inclusion.
|
||||
*/
|
||||
|
||||
#ifndef NAMEPREP_TEMPLATE_INIT
|
||||
#define NAMEPREP_TEMPLATE_INIT
|
||||
|
||||
/* Symbol composition. */
|
||||
#define compose_sym2(a, b) compose_sym2X(a, b)
|
||||
#define compose_sym2X(a, b) a ## b
|
||||
#define compose_sym3(a, b, c) compose_sym3X(a, b, c)
|
||||
#define compose_sym3X(a, b, c) a ## b ## c
|
||||
|
||||
/* The table is based on "Optimized Two-Stage Table" mentioned in
|
||||
* Unicode 3.0 page 106, extended to handle 21bit data instead of 16 bit.
|
||||
*/
|
||||
|
||||
/* Index calculation for multi-level index tables. */
|
||||
#define IDX0(type, v) IDX_0(v, BITS1(type), BITS2(type))
|
||||
#define IDX1(type, v) IDX_1(v, BITS1(type), BITS2(type))
|
||||
#define IDX2(type, v) IDX_2(v, BITS1(type), BITS2(type))
|
||||
|
||||
#define IDX_0(v, bits1, bits2) ((v) >> ((bits1) + (bits2)))
|
||||
#define IDX_1(v, bits1, bits2) (((v) >> (bits2)) & ((1 << (bits1)) - 1))
|
||||
#define IDX_2(v, bits1, bits2) ((v) & ((1 << (bits2)) - 1))
|
||||
|
||||
#define BITS1(type) type ## _BITS_1
|
||||
#define BITS2(type) type ## _BITS_2
|
||||
|
||||
#endif /* NAMEPREP_TEMPLATE_INIT */
|
||||
|
||||
static const char *
|
||||
compose_sym2(nameprep_map_, VERSION) (uint32_t v) {
|
||||
int idx0 = IDX0(MAP, v);
|
||||
int idx1 = IDX1(MAP, v);
|
||||
int idx2 = IDX2(MAP, v);
|
||||
int offset;
|
||||
|
||||
#define IMAP compose_sym3(nameprep_, VERSION, _map_imap)
|
||||
#define TABLE compose_sym3(nameprep_, VERSION, _map_table)
|
||||
#define DATA compose_sym3(nameprep_, VERSION, _map_data)
|
||||
offset = TABLE[IMAP[IMAP[idx0] + idx1]].tbl[idx2];
|
||||
if (offset == 0)
|
||||
return (NULL); /* no mapping */
|
||||
return (const char *)(DATA + offset);
|
||||
#undef IMAP
|
||||
#undef TABLE
|
||||
#undef DATA
|
||||
}
|
||||
|
||||
static int
|
||||
compose_sym2(nameprep_prohibited_, VERSION) (uint32_t v) {
|
||||
int idx0 = IDX0(PROH, v);
|
||||
int idx1 = IDX1(PROH, v);
|
||||
int idx2 = IDX2(PROH, v);
|
||||
const unsigned char *bm;
|
||||
|
||||
#define IMAP compose_sym3(nameprep_, VERSION, _prohibited_imap)
|
||||
#define BITMAP compose_sym3(nameprep_, VERSION, _prohibited_bitmap)
|
||||
bm = BITMAP[IMAP[IMAP[idx0] + idx1]].bm;
|
||||
return (bm[idx2 / 8] & (1 << (idx2 % 8)));
|
||||
#undef IMAP
|
||||
#undef BITMAP
|
||||
}
|
||||
|
||||
static int
|
||||
compose_sym2(nameprep_unassigned_, VERSION) (uint32_t v) {
|
||||
int idx0 = IDX0(UNAS, v);
|
||||
int idx1 = IDX1(UNAS, v);
|
||||
int idx2 = IDX2(UNAS, v);
|
||||
const unsigned char *bm;
|
||||
|
||||
#define IMAP compose_sym3(nameprep_, VERSION, _unassigned_imap)
|
||||
#define BITMAP compose_sym3(nameprep_, VERSION, _unassigned_bitmap)
|
||||
bm = BITMAP[IMAP[IMAP[idx0] + idx1]].bm;
|
||||
return (bm[idx2 / 8] & (1 << (idx2 % 8)));
|
||||
#undef IMAP
|
||||
#undef BITMAP
|
||||
}
|
||||
|
||||
static idn_biditype_t
|
||||
compose_sym2(nameprep_biditype_, VERSION) (uint32_t v) {
|
||||
int idx0 = IDX0(BIDI, v);
|
||||
int idx1 = IDX1(BIDI, v);
|
||||
int idx2 = IDX2(BIDI, v);
|
||||
int offset;
|
||||
|
||||
#define IMAP compose_sym3(nameprep_, VERSION, _bidi_imap)
|
||||
#define TABLE compose_sym3(nameprep_, VERSION, _bidi_table)
|
||||
#define DATA compose_sym3(nameprep_, VERSION, _bidi_data)
|
||||
offset = TABLE[IMAP[IMAP[idx0] + idx1]].tbl[idx2];
|
||||
return DATA[offset];
|
||||
#undef IMAP
|
||||
#undef TABLE
|
||||
#undef DATA
|
||||
}
|
||||
2588
netwerk/dns/nameprepdata.c
Normal file
2588
netwerk/dns/nameprepdata.c
Normal file
File diff suppressed because it is too large
Load diff
1071
netwerk/dns/nsDNSService2.cpp
Normal file
1071
netwerk/dns/nsDNSService2.cpp
Normal file
File diff suppressed because it is too large
Load diff
72
netwerk/dns/nsDNSService2.h
Normal file
72
netwerk/dns/nsDNSService2.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et 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/. */
|
||||
|
||||
#ifndef nsDNSService2_h__
|
||||
#define nsDNSService2_h__
|
||||
|
||||
#include "nsPIDNSService.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsDNSService final : public nsPIDNSService
|
||||
, public nsIObserver
|
||||
, public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSPIDNSSERVICE
|
||||
NS_DECL_NSIDNSSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
nsDNSService();
|
||||
|
||||
static nsIDNSService* GetXPCOMSingleton();
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
bool GetOffline() const;
|
||||
|
||||
private:
|
||||
~nsDNSService();
|
||||
|
||||
static nsDNSService* GetSingleton();
|
||||
|
||||
uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
|
||||
|
||||
nsresult PreprocessHostname(bool aLocalDomain,
|
||||
const nsACString &aInput,
|
||||
nsIIDNService *aIDN,
|
||||
nsACString &aACE);
|
||||
|
||||
RefPtr<nsHostResolver> mResolver;
|
||||
nsCOMPtr<nsIIDNService> mIDN;
|
||||
|
||||
// mLock protects access to mResolver and mIPv4OnlyDomains
|
||||
mozilla::Mutex mLock;
|
||||
|
||||
// mIPv4OnlyDomains is a comma-separated list of domains for which only
|
||||
// IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
|
||||
// a per-domain basis and work around broken DNS servers. See bug 68796.
|
||||
nsAdoptingCString mIPv4OnlyDomains;
|
||||
bool mDisableIPv6;
|
||||
bool mDisablePrefetch;
|
||||
bool mBlockDotOnion;
|
||||
bool mFirstTime;
|
||||
bool mNotifyResolution;
|
||||
bool mOfflineLocalhost;
|
||||
nsTHashtable<nsCStringHashKey> mLocalDomains;
|
||||
};
|
||||
|
||||
#endif //nsDNSService2_h__
|
||||
368
netwerk/dns/nsEffectiveTLDService.cpp
Normal file
368
netwerk/dns/nsEffectiveTLDService.cpp
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// This service reads a file of rules describing TLD-like domain names. For a
|
||||
// complete description of the expected file format and parsing rules, see
|
||||
// http://wiki.mozilla.org/Gecko:Effective_TLD_Service
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsEffectiveTLDService.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService,
|
||||
nsIMemoryReporter)
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#define ETLD_STR_NUM_1(line) str##line
|
||||
#define ETLD_STR_NUM(line) ETLD_STR_NUM_1(line)
|
||||
#define ETLD_ENTRY_OFFSET(name) offsetof(struct etld_string_list, ETLD_STR_NUM(__LINE__))
|
||||
|
||||
const ETLDEntry ETLDEntry::entries[] = {
|
||||
#define ETLD_ENTRY(name, ex, wild) { ETLD_ENTRY_OFFSET(name), ex, wild },
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
};
|
||||
|
||||
const union ETLDEntry::etld_strings ETLDEntry::strings = {
|
||||
{
|
||||
#define ETLD_ENTRY(name, ex, wild) name,
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
}
|
||||
};
|
||||
|
||||
/* static */ const ETLDEntry*
|
||||
ETLDEntry::GetEntry(const char* aDomain)
|
||||
{
|
||||
size_t i;
|
||||
if (BinarySearchIf(entries, 0, ArrayLength(ETLDEntry::entries),
|
||||
Cmp(aDomain), &i)) {
|
||||
return &entries[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Dummy function to statically ensure that our indices don't overflow
|
||||
// the storage provided for them.
|
||||
void
|
||||
ETLDEntry::FuncForStaticAsserts(void)
|
||||
{
|
||||
#define ETLD_ENTRY(name, ex, wild) \
|
||||
static_assert(ETLD_ENTRY_OFFSET(name) < (1 << ETLD_ENTRY_N_INDEX_BITS), \
|
||||
"invalid strtab index");
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
}
|
||||
|
||||
#undef ETLD_ENTRY_OFFSET
|
||||
#undef ETLD_STR_NUM
|
||||
#undef ETLD_STR_NUM1
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static nsEffectiveTLDService *gService = nullptr;
|
||||
|
||||
nsEffectiveTLDService::nsEffectiveTLDService()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsEffectiveTLDService::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Sanity-check the eTLD entries.
|
||||
for (uint32_t i = 0; i < ArrayLength(ETLDEntry::entries); i++) {
|
||||
const char* domain = ETLDEntry::entries[i].GetEffectiveTLDName();
|
||||
nsDependentCString name(domain);
|
||||
nsAutoCString normalizedName(domain);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(NormalizeHostname(normalizedName)),
|
||||
"normalization failure!");
|
||||
MOZ_ASSERT(name.Equals(normalizedName), "domain not normalized!");
|
||||
|
||||
// Domains must be in sorted order for binary search to work.
|
||||
if (i > 0) {
|
||||
const char* domain0 = ETLDEntry::entries[i - 1].GetEffectiveTLDName();
|
||||
MOZ_ASSERT(strcmp(domain0, domain) < 0, "domains not in sorted order!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(!gService);
|
||||
gService = this;
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsEffectiveTLDService::~nsEffectiveTLDService()
|
||||
{
|
||||
UnregisterWeakMemoryReporter(this);
|
||||
gService = nullptr;
|
||||
}
|
||||
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(EffectiveTLDServiceMallocSizeOf)
|
||||
|
||||
// The amount of heap memory measured here is tiny. It used to be bigger when
|
||||
// nsEffectiveTLDService used a separate hash table instead of binary search.
|
||||
// Nonetheless, we keep this code here in anticipation of bug 1083971 which will
|
||||
// change ETLDEntries::entries to a heap-allocated array modifiable at runtime.
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/network/effective-TLD-service", KIND_HEAP, UNITS_BYTES,
|
||||
SizeOfIncludingThis(EffectiveTLDServiceMallocSizeOf),
|
||||
"Memory used by the effective TLD service.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
size_t
|
||||
nsEffectiveTLDService::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
size_t n = aMallocSizeOf(this);
|
||||
|
||||
// Measurement of the following members may be added later if DMD finds it is
|
||||
// worthwhile:
|
||||
// - mIDNService
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// External function for dealing with URI's correctly.
|
||||
// Pulls out the host portion from an nsIURI, and calls through to
|
||||
// GetPublicSuffixFromHost().
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::GetPublicSuffix(nsIURI *aURI,
|
||||
nsACString &aPublicSuffix)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
|
||||
NS_ENSURE_ARG_POINTER(innerURI);
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = innerURI->GetAsciiHost(host);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return GetBaseDomainInternal(host, 0, aPublicSuffix);
|
||||
}
|
||||
|
||||
// External function for dealing with URI's correctly.
|
||||
// Pulls out the host portion from an nsIURI, and calls through to
|
||||
// GetBaseDomainFromHost().
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::GetBaseDomain(nsIURI *aURI,
|
||||
uint32_t aAdditionalParts,
|
||||
nsACString &aBaseDomain)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
NS_ENSURE_TRUE( ((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG);
|
||||
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
|
||||
NS_ENSURE_ARG_POINTER(innerURI);
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = innerURI->GetAsciiHost(host);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return GetBaseDomainInternal(host, aAdditionalParts + 1, aBaseDomain);
|
||||
}
|
||||
|
||||
// External function for dealing with a host string directly: finds the public
|
||||
// suffix (e.g. co.uk) for the given hostname. See GetBaseDomainInternal().
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString &aHostname,
|
||||
nsACString &aPublicSuffix)
|
||||
{
|
||||
// Create a mutable copy of the hostname and normalize it to ACE.
|
||||
// This will fail if the hostname includes invalid characters.
|
||||
nsAutoCString normHostname(aHostname);
|
||||
nsresult rv = NormalizeHostname(normHostname);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return GetBaseDomainInternal(normHostname, 0, aPublicSuffix);
|
||||
}
|
||||
|
||||
// External function for dealing with a host string directly: finds the base
|
||||
// domain (e.g. www.co.uk) for the given hostname and number of subdomain parts
|
||||
// requested. See GetBaseDomainInternal().
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString &aHostname,
|
||||
uint32_t aAdditionalParts,
|
||||
nsACString &aBaseDomain)
|
||||
{
|
||||
NS_ENSURE_TRUE( ((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG);
|
||||
|
||||
// Create a mutable copy of the hostname and normalize it to ACE.
|
||||
// This will fail if the hostname includes invalid characters.
|
||||
nsAutoCString normHostname(aHostname);
|
||||
nsresult rv = NormalizeHostname(normHostname);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return GetBaseDomainInternal(normHostname, aAdditionalParts + 1, aBaseDomain);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEffectiveTLDService::GetNextSubDomain(const nsACString& aHostname,
|
||||
nsACString& aBaseDomain)
|
||||
{
|
||||
// Create a mutable copy of the hostname and normalize it to ACE.
|
||||
// This will fail if the hostname includes invalid characters.
|
||||
nsAutoCString normHostname(aHostname);
|
||||
nsresult rv = NormalizeHostname(normHostname);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return GetBaseDomainInternal(normHostname, -1, aBaseDomain);
|
||||
}
|
||||
|
||||
// Finds the base domain for a host, with requested number of additional parts.
|
||||
// This will fail, generating an error, if the host is an IPv4/IPv6 address,
|
||||
// if more subdomain parts are requested than are available, or if the hostname
|
||||
// includes characters that are not valid in a URL. Normalization is performed
|
||||
// on the host string and the result will be in UTF8.
|
||||
nsresult
|
||||
nsEffectiveTLDService::GetBaseDomainInternal(nsCString &aHostname,
|
||||
int32_t aAdditionalParts,
|
||||
nsACString &aBaseDomain)
|
||||
{
|
||||
if (aHostname.IsEmpty())
|
||||
return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
|
||||
|
||||
// chomp any trailing dot, and keep track of it for later
|
||||
bool trailingDot = aHostname.Last() == '.';
|
||||
if (trailingDot)
|
||||
aHostname.Truncate(aHostname.Length() - 1);
|
||||
|
||||
// check the edge cases of the host being '.' or having a second trailing '.',
|
||||
// since subsequent checks won't catch it.
|
||||
if (aHostname.IsEmpty() || aHostname.Last() == '.')
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Check if we're dealing with an IPv4/IPv6 hostname, and return
|
||||
PRNetAddr addr;
|
||||
PRStatus result = PR_StringToNetAddr(aHostname.get(), &addr);
|
||||
if (result == PR_SUCCESS)
|
||||
return NS_ERROR_HOST_IS_IP_ADDRESS;
|
||||
|
||||
// Walk up the domain tree, most specific to least specific,
|
||||
// looking for matches at each level. Note that a given level may
|
||||
// have multiple attributes (e.g. IsWild() and IsNormal()).
|
||||
const char *prevDomain = nullptr;
|
||||
const char *currDomain = aHostname.get();
|
||||
const char *nextDot = strchr(currDomain, '.');
|
||||
const char *end = currDomain + aHostname.Length();
|
||||
// Default value of *eTLD is currDomain as set in the while loop below
|
||||
const char *eTLD = nullptr;
|
||||
while (true) {
|
||||
// sanity check the string we're about to look up: it should not begin with
|
||||
// a '.'; this would mean the hostname began with a '.' or had an
|
||||
// embedded '..' sequence.
|
||||
if (*currDomain == '.')
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Perform the lookup.
|
||||
const ETLDEntry* entry = ETLDEntry::GetEntry(currDomain);
|
||||
if (entry) {
|
||||
if (entry->IsWild() && prevDomain) {
|
||||
// wildcard rules imply an eTLD one level inferior to the match.
|
||||
eTLD = prevDomain;
|
||||
break;
|
||||
|
||||
} else if (entry->IsNormal() || !nextDot) {
|
||||
// specific match, or we've hit the top domain level
|
||||
eTLD = currDomain;
|
||||
break;
|
||||
|
||||
} else if (entry->IsException()) {
|
||||
// exception rules imply an eTLD one level superior to the match.
|
||||
eTLD = nextDot + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextDot) {
|
||||
// we've hit the top domain level; use it by default.
|
||||
eTLD = currDomain;
|
||||
break;
|
||||
}
|
||||
|
||||
prevDomain = currDomain;
|
||||
currDomain = nextDot + 1;
|
||||
nextDot = strchr(currDomain, '.');
|
||||
}
|
||||
|
||||
const char *begin, *iter;
|
||||
if (aAdditionalParts < 0) {
|
||||
NS_ASSERTION(aAdditionalParts == -1,
|
||||
"aAdditionalParts can't be negative and different from -1");
|
||||
|
||||
for (iter = aHostname.get(); iter != eTLD && *iter != '.'; iter++);
|
||||
|
||||
if (iter != eTLD) {
|
||||
iter++;
|
||||
}
|
||||
if (iter != eTLD) {
|
||||
aAdditionalParts = 0;
|
||||
}
|
||||
} else {
|
||||
// count off the number of requested domains.
|
||||
begin = aHostname.get();
|
||||
iter = eTLD;
|
||||
|
||||
while (true) {
|
||||
if (iter == begin)
|
||||
break;
|
||||
|
||||
if (*(--iter) == '.' && aAdditionalParts-- == 0) {
|
||||
++iter;
|
||||
++aAdditionalParts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aAdditionalParts != 0)
|
||||
return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
|
||||
|
||||
aBaseDomain = Substring(iter, end);
|
||||
// add on the trailing dot, if applicable
|
||||
if (trailingDot)
|
||||
aBaseDomain.Append('.');
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Normalizes the given hostname, component by component. ASCII/ACE
|
||||
// components are lower-cased, and UTF-8 components are normalized per
|
||||
// RFC 3454 and converted to ACE.
|
||||
nsresult
|
||||
nsEffectiveTLDService::NormalizeHostname(nsCString &aHostname)
|
||||
{
|
||||
if (!IsASCII(aHostname)) {
|
||||
nsresult rv = mIDNService->ConvertUTF8toACE(aHostname, aHostname);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
|
||||
ToLowerCase(aHostname);
|
||||
return NS_OK;
|
||||
}
|
||||
98
netwerk/dns/nsEffectiveTLDService.h
Normal file
98
netwerk/dns/nsEffectiveTLDService.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//* -*- 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 EffectiveTLDService_h
|
||||
#define EffectiveTLDService_h
|
||||
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
class nsIIDNService;
|
||||
|
||||
// struct for static data generated from effective_tld_names.dat
|
||||
struct ETLDEntry {
|
||||
friend class nsEffectiveTLDService;
|
||||
|
||||
public:
|
||||
bool IsNormal() const { return wild || !exception; }
|
||||
bool IsException() const { return exception; }
|
||||
bool IsWild() const { return wild; }
|
||||
|
||||
const char* GetEffectiveTLDName() const
|
||||
{
|
||||
return strings.strtab + strtab_index;
|
||||
}
|
||||
|
||||
static const ETLDEntry* GetEntry(const char* aDomain);
|
||||
|
||||
static const size_t ETLD_ENTRY_N_INDEX_BITS = 30;
|
||||
|
||||
// These fields must be public to allow static construction.
|
||||
uint32_t strtab_index : ETLD_ENTRY_N_INDEX_BITS;
|
||||
uint32_t exception : 1;
|
||||
uint32_t wild : 1;
|
||||
|
||||
private:
|
||||
struct Cmp {
|
||||
int operator()(const ETLDEntry aEntry) const
|
||||
{
|
||||
return strcmp(mName, aEntry.GetEffectiveTLDName());
|
||||
}
|
||||
explicit Cmp(const char* aName) : mName(aName) {}
|
||||
const char* mName;
|
||||
};
|
||||
|
||||
#define ETLD_STR_NUM_1(line) str##line
|
||||
#define ETLD_STR_NUM(line) ETLD_STR_NUM_1(line)
|
||||
struct etld_string_list {
|
||||
#define ETLD_ENTRY(name, ex, wild) char ETLD_STR_NUM(__LINE__)[sizeof(name)];
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
};
|
||||
|
||||
// This static string table is all the eTLD domain names packed together.
|
||||
static const union etld_strings {
|
||||
struct etld_string_list list;
|
||||
char strtab[1];
|
||||
} strings;
|
||||
|
||||
// This is the static entries table. Each entry has an index into the string
|
||||
// table. The entries are in sorted order so that binary search can be used.
|
||||
static const ETLDEntry entries[];
|
||||
|
||||
void FuncForStaticAsserts(void);
|
||||
#undef ETLD_STR_NUM
|
||||
#undef ETLD_STR_NUM1
|
||||
};
|
||||
|
||||
class nsEffectiveTLDService final
|
||||
: public nsIEffectiveTLDService
|
||||
, public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEFFECTIVETLDSERVICE
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
nsEffectiveTLDService();
|
||||
nsresult Init();
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
private:
|
||||
nsresult GetBaseDomainInternal(nsCString &aHostname, int32_t aAdditionalParts, nsACString &aBaseDomain);
|
||||
nsresult NormalizeHostname(nsCString &aHostname);
|
||||
~nsEffectiveTLDService();
|
||||
|
||||
nsCOMPtr<nsIIDNService> mIDNService;
|
||||
};
|
||||
|
||||
#endif // EffectiveTLDService_h
|
||||
1579
netwerk/dns/nsHostResolver.cpp
Normal file
1579
netwerk/dns/nsHostResolver.cpp
Normal file
File diff suppressed because it is too large
Load diff
372
netwerk/dns/nsHostResolver.h
Normal file
372
netwerk/dns/nsHostResolver.h
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
/* vim:set ts=4 sw=4 sts=4 et cin: */
|
||||
/* 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 nsHostResolver_h__
|
||||
#define nsHostResolver_h__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "prclist.h"
|
||||
#include "prnetdb.h"
|
||||
#include "PLDHashTable.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "GetAddrInfo.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "mozilla/net/DashboardTypes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
class nsHostResolver;
|
||||
class nsHostRecord;
|
||||
class nsResolveHostCallback;
|
||||
|
||||
#define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY 3
|
||||
#define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 5
|
||||
#define MAX_NON_PRIORITY_REQUESTS 150
|
||||
|
||||
#define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \
|
||||
MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY)
|
||||
|
||||
struct nsHostKey
|
||||
{
|
||||
const char *host;
|
||||
uint16_t flags;
|
||||
uint16_t af;
|
||||
const char *netInterface;
|
||||
};
|
||||
|
||||
/**
|
||||
* nsHostRecord - ref counted object type stored in host resolver cache.
|
||||
*/
|
||||
class nsHostRecord : public PRCList, public nsHostKey
|
||||
{
|
||||
typedef mozilla::Mutex Mutex;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostRecord)
|
||||
|
||||
/* instantiates a new host record */
|
||||
static nsresult Create(const nsHostKey *key, nsHostRecord **record);
|
||||
|
||||
/* a fully resolved host record has either a non-null |addr_info| or |addr|
|
||||
* field. if |addr_info| is null, it implies that the |host| is an IP
|
||||
* address literal. in which case, |addr| contains the parsed address.
|
||||
* otherwise, if |addr_info| is non-null, then it contains one or many
|
||||
* IP addresses corresponding to the given host name. if both |addr_info|
|
||||
* and |addr| are null, then the given host has not yet been fully resolved.
|
||||
* |af| is the address family of the record we are querying for.
|
||||
*/
|
||||
|
||||
/* the lock protects |addr_info| and |addr_info_gencnt| because they
|
||||
* are mutable and accessed by the resolver worker thread and the
|
||||
* nsDNSService2 class. |addr| doesn't change after it has been
|
||||
* assigned a value. only the resolver worker thread modifies
|
||||
* nsHostRecord (and only in nsHostResolver::OnLookupComplete);
|
||||
* the other threads just read it. therefore the resolver worker
|
||||
* thread doesn't need to lock when reading |addr_info|.
|
||||
*/
|
||||
Mutex addr_info_lock;
|
||||
int addr_info_gencnt; /* generation count of |addr_info| */
|
||||
mozilla::net::AddrInfo *addr_info;
|
||||
mozilla::net::NetAddr *addr;
|
||||
bool negative; /* True if this record is a cache of a failed lookup.
|
||||
Negative cache entries are valid just like any other
|
||||
(though never for more than 60 seconds), but a use
|
||||
of that negative entry forces an asynchronous refresh. */
|
||||
|
||||
enum ExpirationStatus {
|
||||
EXP_VALID,
|
||||
EXP_GRACE,
|
||||
EXP_EXPIRED,
|
||||
};
|
||||
|
||||
ExpirationStatus CheckExpiration(const mozilla::TimeStamp& now) const;
|
||||
|
||||
// When the record began being valid. Used mainly for bookkeeping.
|
||||
mozilla::TimeStamp mValidStart;
|
||||
|
||||
// When the record is no longer valid (it's time of expiration)
|
||||
mozilla::TimeStamp mValidEnd;
|
||||
|
||||
// When the record enters its grace period. This must be before mValidEnd.
|
||||
// If a record is in its grace period (and not expired), it will be used
|
||||
// but a request to refresh it will be made.
|
||||
mozilla::TimeStamp mGraceStart;
|
||||
|
||||
// Convenience function for setting the timestamps above (mValidStart,
|
||||
// mValidEnd, and mGraceStart). valid and grace are durations in seconds.
|
||||
void SetExpiration(const mozilla::TimeStamp& now, unsigned int valid,
|
||||
unsigned int grace);
|
||||
void CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord);
|
||||
|
||||
// Checks if the record is usable (not expired and has a value)
|
||||
bool HasUsableResult(const mozilla::TimeStamp& now, uint16_t queryFlags = 0) const;
|
||||
|
||||
// hold addr_info_lock when calling the blacklist functions
|
||||
bool Blacklisted(mozilla::net::NetAddr *query);
|
||||
void ResetBlacklist();
|
||||
void ReportUnusable(mozilla::net::NetAddr *addr);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
enum DnsPriority {
|
||||
DNS_PRIORITY_LOW,
|
||||
DNS_PRIORITY_MEDIUM,
|
||||
DNS_PRIORITY_HIGH,
|
||||
};
|
||||
static DnsPriority GetPriority(uint16_t aFlags);
|
||||
|
||||
bool RemoveOrRefresh(); // Mark records currently being resolved as needed
|
||||
// to resolve again.
|
||||
|
||||
private:
|
||||
friend class nsHostResolver;
|
||||
|
||||
|
||||
PRCList callbacks; /* list of callbacks */
|
||||
|
||||
bool resolving; /* true if this record is being resolved, which means
|
||||
* that it is either on the pending queue or owned by
|
||||
* one of the worker threads. */
|
||||
|
||||
bool onQueue; /* true if pending and on the queue (not yet given to getaddrinfo())*/
|
||||
bool usingAnyThread; /* true if off queue and contributing to mActiveAnyThreadCount */
|
||||
bool mDoomed; /* explicitly expired */
|
||||
|
||||
#if TTL_AVAILABLE
|
||||
bool mGetTtl;
|
||||
#endif
|
||||
|
||||
// The number of times ReportUnusable() has been called in the record's
|
||||
// lifetime.
|
||||
uint32_t mBlacklistedCount;
|
||||
|
||||
// when the results from this resolve is returned, it is not to be
|
||||
// trusted, but instead a new resolve must be made!
|
||||
bool mResolveAgain;
|
||||
|
||||
// a list of addresses associated with this record that have been reported
|
||||
// as unusable. the list is kept as a set of strings to make it independent
|
||||
// of gencnt.
|
||||
nsTArray<nsCString> mBlacklistedItems;
|
||||
|
||||
explicit nsHostRecord(const nsHostKey *key); /* use Create() instead */
|
||||
~nsHostRecord();
|
||||
};
|
||||
|
||||
/**
|
||||
* ResolveHost callback object. It's PRCList members are used by
|
||||
* the nsHostResolver and should not be used by anything else.
|
||||
*/
|
||||
class NS_NO_VTABLE nsResolveHostCallback : public PRCList
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* OnLookupComplete
|
||||
*
|
||||
* this function is called to complete a host lookup initiated by
|
||||
* nsHostResolver::ResolveHost. it may be invoked recursively from
|
||||
* ResolveHost or on an unspecified background thread.
|
||||
*
|
||||
* NOTE: it is the responsibility of the implementor of this method
|
||||
* to handle the callback in a thread safe manner.
|
||||
*
|
||||
* @param resolver
|
||||
* nsHostResolver object associated with this result
|
||||
* @param record
|
||||
* the host record containing the results of the lookup
|
||||
* @param status
|
||||
* if successful, |record| contains non-null results
|
||||
*/
|
||||
virtual void OnLookupComplete(nsHostResolver *resolver,
|
||||
nsHostRecord *record,
|
||||
nsresult status) = 0;
|
||||
/**
|
||||
* EqualsAsyncListener
|
||||
*
|
||||
* Determines if the listener argument matches the listener member var.
|
||||
* For subclasses not implementing a member listener, should return false.
|
||||
* For subclasses having a member listener, the function should check if
|
||||
* they are the same. Used for cases where a pointer to an object
|
||||
* implementing nsResolveHostCallback is unknown, but a pointer to
|
||||
* the original listener is known.
|
||||
*
|
||||
* @param aListener
|
||||
* nsIDNSListener object associated with the original request
|
||||
*/
|
||||
virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0;
|
||||
|
||||
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* nsHostResolver - an asynchronous host name resolver.
|
||||
*/
|
||||
class nsHostResolver
|
||||
{
|
||||
typedef mozilla::CondVar CondVar;
|
||||
typedef mozilla::Mutex Mutex;
|
||||
|
||||
public:
|
||||
/**
|
||||
* host resolver instances are reference counted.
|
||||
*/
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostResolver)
|
||||
|
||||
/**
|
||||
* creates an addref'd instance of a nsHostResolver object.
|
||||
*/
|
||||
static nsresult Create(uint32_t maxCacheEntries, // zero disables cache
|
||||
uint32_t defaultCacheEntryLifetime, // seconds
|
||||
uint32_t defaultGracePeriod, // seconds
|
||||
nsHostResolver **resolver);
|
||||
|
||||
/**
|
||||
* puts the resolver in the shutdown state, which will cause any pending
|
||||
* callbacks to be detached. any future calls to ResolveHost will fail.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* resolve the given hostname asynchronously. the caller can synthesize
|
||||
* a synchronous host lookup using a lock and a cvar. as noted above
|
||||
* the callback will occur re-entrantly from an unspecified thread. the
|
||||
* host lookup cannot be canceled (cancelation can be layered above this
|
||||
* by having the callback implementation return without doing anything).
|
||||
*/
|
||||
nsresult ResolveHost(const char *hostname,
|
||||
uint16_t flags,
|
||||
uint16_t af,
|
||||
const char *netInterface,
|
||||
nsResolveHostCallback *callback);
|
||||
|
||||
/**
|
||||
* removes the specified callback from the nsHostRecord for the given
|
||||
* hostname, flags, and address family. these parameters should correspond
|
||||
* to the parameters passed to ResolveHost. this function executes the
|
||||
* callback if the callback is still pending with the given status.
|
||||
*/
|
||||
void DetachCallback(const char *hostname,
|
||||
uint16_t flags,
|
||||
uint16_t af,
|
||||
const char *netInterface,
|
||||
nsResolveHostCallback *callback,
|
||||
nsresult status);
|
||||
|
||||
/**
|
||||
* Cancels an async request associated with the hostname, flags,
|
||||
* address family and listener. Cancels first callback found which matches
|
||||
* these criteria. These parameters should correspond to the parameters
|
||||
* passed to ResolveHost. If this is the last callback associated with the
|
||||
* host record, it is removed from any request queues it might be on.
|
||||
*/
|
||||
void CancelAsyncRequest(const char *host,
|
||||
uint16_t flags,
|
||||
uint16_t af,
|
||||
const char *netInterface,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult status);
|
||||
/**
|
||||
* values for the flags parameter passed to ResolveHost and DetachCallback
|
||||
* that may be bitwise OR'd together.
|
||||
*
|
||||
* NOTE: in this implementation, these flags correspond exactly in value
|
||||
* to the flags defined on nsIDNSService.
|
||||
*/
|
||||
enum {
|
||||
RES_BYPASS_CACHE = nsIDNSService::RESOLVE_BYPASS_CACHE,
|
||||
RES_CANON_NAME = nsIDNSService::RESOLVE_CANONICAL_NAME,
|
||||
RES_PRIORITY_MEDIUM = nsIDNSService::RESOLVE_PRIORITY_MEDIUM,
|
||||
RES_PRIORITY_LOW = nsIDNSService::RESOLVE_PRIORITY_LOW,
|
||||
RES_SPECULATE = nsIDNSService::RESOLVE_SPECULATE,
|
||||
//RES_DISABLE_IPV6 = nsIDNSService::RESOLVE_DISABLE_IPV6, // Not used
|
||||
RES_OFFLINE = nsIDNSService::RESOLVE_OFFLINE,
|
||||
//RES_DISABLE_IPv4 = nsIDNSService::RESOLVE_DISABLE_IPV4, // Not Used
|
||||
RES_ALLOW_NAME_COLLISION = nsIDNSService::RESOLVE_ALLOW_NAME_COLLISION
|
||||
};
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
/**
|
||||
* Flush the DNS cache.
|
||||
*/
|
||||
void FlushCache();
|
||||
|
||||
private:
|
||||
explicit nsHostResolver(uint32_t maxCacheEntries,
|
||||
uint32_t defaultCacheEntryLifetime,
|
||||
uint32_t defaultGracePeriod);
|
||||
~nsHostResolver();
|
||||
|
||||
nsresult Init();
|
||||
nsresult IssueLookup(nsHostRecord *);
|
||||
bool GetHostToLookup(nsHostRecord **m);
|
||||
|
||||
enum LookupStatus {
|
||||
LOOKUP_OK,
|
||||
LOOKUP_RESOLVEAGAIN,
|
||||
};
|
||||
|
||||
LookupStatus OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *);
|
||||
void DeQueue(PRCList &aQ, nsHostRecord **aResult);
|
||||
void ClearPendingQueue(PRCList *aPendingQueue);
|
||||
nsresult ConditionallyCreateThread(nsHostRecord *rec);
|
||||
|
||||
/**
|
||||
* Starts a new lookup in the background for entries that are in the grace
|
||||
* period with a failed connect or all cached entries are negative.
|
||||
*/
|
||||
nsresult ConditionallyRefreshRecord(nsHostRecord *rec, const char *host);
|
||||
|
||||
static void MoveQueue(nsHostRecord *aRec, PRCList &aDestQ);
|
||||
|
||||
static void ThreadFunc(void *);
|
||||
|
||||
enum {
|
||||
METHOD_HIT = 1,
|
||||
METHOD_RENEWAL = 2,
|
||||
METHOD_NEGATIVE_HIT = 3,
|
||||
METHOD_LITERAL = 4,
|
||||
METHOD_OVERFLOW = 5,
|
||||
METHOD_NETWORK_FIRST = 6,
|
||||
METHOD_NETWORK_SHARED = 7
|
||||
};
|
||||
|
||||
uint32_t mMaxCacheEntries;
|
||||
uint32_t mDefaultCacheLifetime; // granularity seconds
|
||||
uint32_t mDefaultGracePeriod; // granularity seconds
|
||||
mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const
|
||||
CondVar mIdleThreadCV;
|
||||
PLDHashTable mDB;
|
||||
PRCList mHighQ;
|
||||
PRCList mMediumQ;
|
||||
PRCList mLowQ;
|
||||
PRCList mEvictionQ;
|
||||
uint32_t mEvictionQSize;
|
||||
PRTime mCreationTime;
|
||||
PRIntervalTime mLongIdleTimeout;
|
||||
PRIntervalTime mShortIdleTimeout;
|
||||
|
||||
mozilla::Atomic<bool> mShutdown;
|
||||
mozilla::Atomic<uint32_t> mNumIdleThreads;
|
||||
mozilla::Atomic<uint32_t> mThreadCount;
|
||||
mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
|
||||
mozilla::Atomic<uint32_t> mPendingCount;
|
||||
|
||||
// Set the expiration time stamps appropriately.
|
||||
void PrepareRecordExpiration(nsHostRecord* rec) const;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Called by the networking dashboard via the DnsService2
|
||||
*/
|
||||
void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *);
|
||||
};
|
||||
|
||||
#endif // nsHostResolver_h__
|
||||
195
netwerk/dns/nsIDNKitInterface.h
Normal file
195
netwerk/dns/nsIDNKitInterface.h
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved.
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set forth bellow.
|
||||
*
|
||||
* LICENSE TERMS AND CONDITIONS
|
||||
*
|
||||
* The following License Terms and Conditions apply, unless a different
|
||||
* license is obtained from Japan Network Information Center ("JPNIC"),
|
||||
* a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
|
||||
* Chiyoda-ku, Tokyo 101-0047, Japan.
|
||||
|
||||
* 1. Use, Modification and Redistribution (including distribution of any
|
||||
* modified or derived work) in source and/or binary forms is permitted
|
||||
* under this License Terms and Conditions.
|
||||
*
|
||||
* 2. Redistribution of source code must retain the copyright notices as they
|
||||
* appear in each source code file, this License Terms and Conditions.
|
||||
*
|
||||
* 3. Redistribution in binary form must reproduce the Copyright Notice,
|
||||
* this License Terms and Conditions, in the documentation and/or other
|
||||
* materials provided with the distribution. For the purposes of binary
|
||||
* distribution the "Copyright Notice" refers to the following language:
|
||||
* "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
|
||||
*
|
||||
* 4. The name of JPNIC may not be used to endorse or promote products
|
||||
* derived from this Software without specific prior written approval of
|
||||
* JPNIC.
|
||||
*
|
||||
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#ifndef nsIDNKitWrapper_h__
|
||||
#define nsIDNKitWrapper_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* libidnkit result code.
|
||||
*/
|
||||
typedef enum {
|
||||
idn_success,
|
||||
idn_notfound,
|
||||
idn_invalid_encoding,
|
||||
idn_invalid_syntax,
|
||||
idn_invalid_name,
|
||||
idn_invalid_message,
|
||||
idn_invalid_action,
|
||||
idn_invalid_codepoint,
|
||||
idn_invalid_length,
|
||||
idn_buffer_overflow,
|
||||
idn_noentry,
|
||||
idn_nomemory,
|
||||
idn_nofile,
|
||||
idn_nomapping,
|
||||
idn_context_required,
|
||||
idn_prohibited,
|
||||
idn_failure /* !!This must be the last one!! */
|
||||
} idn_result_t;
|
||||
|
||||
/*
|
||||
* BIDI type codes.
|
||||
*/
|
||||
typedef enum {
|
||||
idn_biditype_r_al,
|
||||
idn_biditype_l,
|
||||
idn_biditype_others
|
||||
} idn_biditype_t;
|
||||
|
||||
/*
|
||||
* A Handle for nameprep operations.
|
||||
*/
|
||||
typedef struct idn_nameprep *idn_nameprep_t;
|
||||
|
||||
|
||||
/*
|
||||
* The latest version of nameprep.
|
||||
*/
|
||||
#define IDN_NAMEPREP_CURRENT "nameprep-11"
|
||||
|
||||
#undef assert
|
||||
#define assert(a)
|
||||
#define TRACE(a)
|
||||
|
||||
|
||||
/* race.c */
|
||||
idn_result_t race_decode_decompress(const char *from,
|
||||
uint16_t *buf,
|
||||
size_t buflen);
|
||||
idn_result_t race_compress_encode(const uint16_t *p,
|
||||
int compress_mode,
|
||||
char *to, size_t tolen);
|
||||
int get_compress_mode(uint16_t *p);
|
||||
|
||||
|
||||
/* nameprep.c */
|
||||
|
||||
/*
|
||||
* Create a handle for nameprep operations.
|
||||
* The handle is stored in '*handlep', which is used other functions
|
||||
* in this module.
|
||||
* The version of the NAMEPREP specification can be specified with
|
||||
* 'version' parameter. If 'version' is nullptr, the latest version
|
||||
* is used.
|
||||
*
|
||||
* Returns:
|
||||
* idn_success -- ok.
|
||||
* idn_notfound -- specified version not found.
|
||||
*/
|
||||
idn_result_t
|
||||
idn_nameprep_create(const char *version, idn_nameprep_t *handlep);
|
||||
|
||||
/*
|
||||
* Close a handle, which was created by 'idn_nameprep_create'.
|
||||
*/
|
||||
void
|
||||
idn_nameprep_destroy(idn_nameprep_t handle);
|
||||
|
||||
/*
|
||||
* Perform character mapping on an UCS4 string specified by 'from', and
|
||||
* store the result into 'to', whose length is specified by 'tolen'.
|
||||
*
|
||||
* Returns:
|
||||
* idn_success -- ok.
|
||||
* idn_buffer_overflow -- result buffer is too small.
|
||||
*/
|
||||
idn_result_t
|
||||
idn_nameprep_map(idn_nameprep_t handle, const uint32_t *from,
|
||||
uint32_t *to, size_t tolen);
|
||||
|
||||
/*
|
||||
* Check if an UCS4 string 'str' contains any prohibited characters specified
|
||||
* by the draft. If found, the pointer to the first such character is stored
|
||||
* into '*found'. Otherwise '*found' will be nullptr.
|
||||
*
|
||||
* Returns:
|
||||
* idn_success -- check has been done properly. (But this
|
||||
* does not mean that no prohibited character
|
||||
* was found. Check '*found' to see the
|
||||
* result.)
|
||||
*/
|
||||
idn_result_t
|
||||
idn_nameprep_isprohibited(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found);
|
||||
|
||||
/*
|
||||
* Check if an UCS4 string 'str' contains any unassigned characters specified
|
||||
* by the draft. If found, the pointer to the first such character is stored
|
||||
* into '*found'. Otherwise '*found' will be nullptr.
|
||||
*
|
||||
* Returns:
|
||||
* idn_success -- check has been done properly. (But this
|
||||
* does not mean that no unassinged character
|
||||
* was found. Check '*found' to see the
|
||||
* result.)
|
||||
*/
|
||||
idn_result_t
|
||||
idn_nameprep_isunassigned(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found);
|
||||
|
||||
/*
|
||||
* Check if an UCS4 string 'str' is valid string specified by ``bidi check''
|
||||
* of the draft. If it is not valid, the pointer to the first invalid
|
||||
* character is stored into '*found'. Otherwise '*found' will be nullptr.
|
||||
*
|
||||
* Returns:
|
||||
* idn_success -- check has been done properly. (But this
|
||||
* does not mean that the string was valid.
|
||||
* Check '*found' to see the result.)
|
||||
*/
|
||||
idn_result_t
|
||||
idn_nameprep_isvalidbidi(idn_nameprep_t handle, const uint32_t *str,
|
||||
const uint32_t **found);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* nsIDNKitWrapper_h__ */
|
||||
46
netwerk/dns/nsIDNSListener.idl
Normal file
46
netwerk/dns/nsIDNSListener.idl
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsICancelable;
|
||||
interface nsIDNSRecord;
|
||||
|
||||
/**
|
||||
* nsIDNSListener
|
||||
*/
|
||||
[scriptable, function, uuid(27d49bfe-280c-49e0-bbaa-f6200c232c3d)]
|
||||
interface nsIDNSListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* called when an asynchronous host lookup completes.
|
||||
*
|
||||
* @param aRequest
|
||||
* the value returned from asyncResolve.
|
||||
* @param aRecord
|
||||
* the DNS record corresponding to the hostname that was resolved.
|
||||
* this parameter is null if there was an error.
|
||||
* @param aStatus
|
||||
* if the lookup failed, this parameter gives the reason.
|
||||
*/
|
||||
void onLookupComplete(in nsICancelable aRequest,
|
||||
in nsIDNSRecord aRecord,
|
||||
in nsresult aStatus);
|
||||
};
|
||||
|
||||
/**
|
||||
* nsIDNSListenerProxy:
|
||||
*
|
||||
* Must be implemented by classes that wrap the original listener passed to
|
||||
* nsIDNSService.AsyncResolve, so we have access to original listener for
|
||||
* comparison purposes.
|
||||
*/
|
||||
[uuid(60eff0e4-6f7c-493c-add9-1cbea59063ad)]
|
||||
interface nsIDNSListenerProxy : nsISupports
|
||||
{
|
||||
/*
|
||||
* The original nsIDNSListener which requested hostname resolution.
|
||||
*/
|
||||
readonly attribute nsIDNSListener originalListener;
|
||||
};
|
||||
100
netwerk/dns/nsIDNSRecord.idl
Normal file
100
netwerk/dns/nsIDNSRecord.idl
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
%{ C++
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
union NetAddr;
|
||||
}
|
||||
}
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
%}
|
||||
native NetAddr(mozilla::net::NetAddr);
|
||||
[ref] native nsNetAddrTArrayRef(nsTArray<mozilla::net::NetAddr>);
|
||||
interface nsINetAddr;
|
||||
|
||||
/**
|
||||
* nsIDNSRecord
|
||||
*
|
||||
* this interface represents the result of a DNS lookup. since a DNS
|
||||
* query may return more than one resolved IP address, the record acts
|
||||
* like an enumerator, allowing the caller to easily step through the
|
||||
* list of IP addresses.
|
||||
*/
|
||||
[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)]
|
||||
interface nsIDNSRecord : nsISupports
|
||||
{
|
||||
/**
|
||||
* @return the canonical hostname for this record. this value is empty if
|
||||
* the record was not fetched with the RESOLVE_CANONICAL_NAME flag.
|
||||
*
|
||||
* e.g., www.mozilla.org --> rheet.mozilla.org
|
||||
*/
|
||||
readonly attribute ACString canonicalName;
|
||||
|
||||
/**
|
||||
* this function copies the value of the next IP address into the
|
||||
* given NetAddr struct and increments the internal address iterator.
|
||||
*
|
||||
* @param aPort
|
||||
* A port number to initialize the NetAddr with.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
|
||||
* the record.
|
||||
*/
|
||||
[noscript] NetAddr getNextAddr(in uint16_t aPort);
|
||||
|
||||
/**
|
||||
* this function copies the value of all working members of the RR
|
||||
* set into the output array.
|
||||
*
|
||||
* @param aAddressArray
|
||||
* The result set
|
||||
*/
|
||||
[noscript] void getAddresses(out nsNetAddrTArrayRef aAddressArray);
|
||||
|
||||
/**
|
||||
* this function returns the value of the next IP address as a
|
||||
* scriptable address and increments the internal address iterator.
|
||||
*
|
||||
* @param aPort
|
||||
* A port number to initialize the nsINetAddr with.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
|
||||
* the record.
|
||||
*/
|
||||
nsINetAddr getScriptableNextAddr(in uint16_t aPort);
|
||||
|
||||
/**
|
||||
* this function returns the value of the next IP address as a
|
||||
* string and increments the internal address iterator.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in
|
||||
* the record.
|
||||
*/
|
||||
ACString getNextAddrAsString();
|
||||
|
||||
/**
|
||||
* this function returns true if there is another address in the record.
|
||||
*/
|
||||
boolean hasMore();
|
||||
|
||||
/**
|
||||
* this function resets the internal address iterator to the first
|
||||
* address in the record.
|
||||
*/
|
||||
void rewind();
|
||||
|
||||
/**
|
||||
* This function indicates that the last address obtained via getNextAddr*()
|
||||
* was not usuable and should be skipped in future uses of this
|
||||
* record if other addresses are available.
|
||||
*
|
||||
* @param aPort is the port number associated with the failure, if any.
|
||||
* It may be zero if not applicable.
|
||||
*/
|
||||
void reportUnusable(in uint16_t aPort);
|
||||
};
|
||||
171
netwerk/dns/nsIDNSService.idl
Normal file
171
netwerk/dns/nsIDNSService.idl
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsICancelable;
|
||||
interface nsIEventTarget;
|
||||
interface nsIDNSRecord;
|
||||
interface nsIDNSListener;
|
||||
|
||||
%{C++
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
namespace mozilla { namespace net {
|
||||
struct DNSCacheEntries;
|
||||
} }
|
||||
%}
|
||||
|
||||
[ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>);
|
||||
|
||||
/**
|
||||
* nsIDNSService
|
||||
*/
|
||||
[scriptable, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)]
|
||||
interface nsIDNSService : nsISupports
|
||||
{
|
||||
/**
|
||||
* kicks off an asynchronous host lookup.
|
||||
*
|
||||
* @param aHostName
|
||||
* the hostname or IP-address-literal to resolve.
|
||||
* @param aFlags
|
||||
* a bitwise OR of the RESOLVE_ prefixed constants defined below.
|
||||
* @param aListener
|
||||
* the listener to be notified when the result is available.
|
||||
* @param aListenerTarget
|
||||
* optional parameter (may be null). if non-null, this parameter
|
||||
* specifies the nsIEventTarget of the thread on which the
|
||||
* listener's onLookupComplete should be called. however, if this
|
||||
* parameter is null, then onLookupComplete will be called on an
|
||||
* unspecified thread (possibly recursively).
|
||||
*
|
||||
* @return An object that can be used to cancel the host lookup.
|
||||
*/
|
||||
nsICancelable asyncResolve(in AUTF8String aHostName,
|
||||
in unsigned long aFlags,
|
||||
in nsIDNSListener aListener,
|
||||
in nsIEventTarget aListenerTarget);
|
||||
|
||||
/**
|
||||
* Attempts to cancel a previously requested async DNS lookup
|
||||
*
|
||||
* @param aHostName
|
||||
* the hostname or IP-address-literal to resolve.
|
||||
* @param aFlags
|
||||
* a bitwise OR of the RESOLVE_ prefixed constants defined below.
|
||||
* @param aListener
|
||||
* the original listener which was to be notified about the host lookup
|
||||
* result - used to match request information to requestor.
|
||||
* @param aReason
|
||||
* nsresult reason for the cancellation
|
||||
*
|
||||
* @return An object that can be used to cancel the host lookup.
|
||||
*/
|
||||
void cancelAsyncResolve(in AUTF8String aHostName,
|
||||
in unsigned long aFlags,
|
||||
in nsIDNSListener aListener,
|
||||
in nsresult aReason);
|
||||
|
||||
/**
|
||||
* called to synchronously resolve a hostname. warning this method may
|
||||
* block the calling thread for a long period of time. it is extremely
|
||||
* unwise to call this function on the UI thread of an application.
|
||||
*
|
||||
* @param aHostName
|
||||
* the hostname or IP-address-literal to resolve.
|
||||
* @param aFlags
|
||||
* a bitwise OR of the RESOLVE_ prefixed constants defined below.
|
||||
*
|
||||
* @return DNS record corresponding to the given hostname.
|
||||
* @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved.
|
||||
*/
|
||||
nsIDNSRecord resolve(in AUTF8String aHostName,
|
||||
in unsigned long aFlags);
|
||||
|
||||
/**
|
||||
* kicks off an asynchronous host lookup.
|
||||
*
|
||||
* This function is identical to asyncResolve except an additional
|
||||
* parameter aNetwortInterface. If parameter aNetworkInterface is an empty
|
||||
* string function will return the same result as asyncResolve.
|
||||
* Setting aNetworkInterface value make only sense for gonk,because it
|
||||
* an per networking interface query is possible.
|
||||
*/
|
||||
nsICancelable asyncResolveExtended(in AUTF8String aHostName,
|
||||
in unsigned long aFlags,
|
||||
in AUTF8String aNetworkInterface,
|
||||
in nsIDNSListener aListener,
|
||||
in nsIEventTarget aListenerTarget);
|
||||
|
||||
/**
|
||||
* Attempts to cancel a previously requested async DNS lookup
|
||||
* This is an extended versin with a additional parameter aNetworkInterface
|
||||
*/
|
||||
void cancelAsyncResolveExtended(in AUTF8String aHostName,
|
||||
in unsigned long aFlags,
|
||||
in AUTF8String aNetworkInterface,
|
||||
in nsIDNSListener aListener,
|
||||
in nsresult aReason);
|
||||
|
||||
/**
|
||||
* The method takes a pointer to an nsTArray
|
||||
* and fills it with cache entry data
|
||||
* Called by the networking dashboard
|
||||
*/
|
||||
[noscript] void getDNSCacheEntries(in EntriesArray args);
|
||||
|
||||
/**
|
||||
* @return the hostname of the operating system.
|
||||
*/
|
||||
readonly attribute AUTF8String myHostName;
|
||||
|
||||
/*************************************************************************
|
||||
* Listed below are the various flags that may be OR'd together to form
|
||||
* the aFlags parameter passed to asyncResolve() and resolve().
|
||||
*/
|
||||
|
||||
/**
|
||||
* if set, this flag suppresses the internal DNS lookup cache.
|
||||
*/
|
||||
const unsigned long RESOLVE_BYPASS_CACHE = (1 << 0);
|
||||
|
||||
/**
|
||||
* if set, the canonical name of the specified host will be queried.
|
||||
*/
|
||||
const unsigned long RESOLVE_CANONICAL_NAME = (1 << 1);
|
||||
|
||||
/**
|
||||
* if set, the query is given lower priority. Medium takes precedence
|
||||
* if both are used.
|
||||
*/
|
||||
const unsigned long RESOLVE_PRIORITY_MEDIUM = (1 << 2);
|
||||
const unsigned long RESOLVE_PRIORITY_LOW = (1 << 3);
|
||||
|
||||
/**
|
||||
* if set, indicates request is speculative. Speculative requests
|
||||
* return errors if prefetching is disabled by configuration.
|
||||
*/
|
||||
const unsigned long RESOLVE_SPECULATE = (1 << 4);
|
||||
|
||||
/**
|
||||
* If set, only IPv4 addresses will be returned from resolve/asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_DISABLE_IPV6 = (1 << 5);
|
||||
|
||||
/**
|
||||
* If set, only literals and cached entries will be returned from resolve/
|
||||
* asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_OFFLINE = (1 << 6);
|
||||
|
||||
/**
|
||||
* If set, only IPv6 addresses will be returned from resolve/asyncResolve.
|
||||
*/
|
||||
const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7);
|
||||
|
||||
/**
|
||||
* If set, allow name collision results (127.0.53.53) which are normally filtered.
|
||||
*/
|
||||
const unsigned long RESOLVE_ALLOW_NAME_COLLISION = (1 << 8);
|
||||
};
|
||||
959
netwerk/dns/nsIDNService.cpp
Normal file
959
netwerk/dns/nsIDNService.cpp
Normal file
|
|
@ -0,0 +1,959 @@
|
|||
/* -*- 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 "nsIDNService.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsUnicodeProperties.h"
|
||||
#include "nsUnicodeScriptCodes.h"
|
||||
#include "harfbuzz/hb.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "punycode.h"
|
||||
|
||||
#ifdef IDNA2008
|
||||
// Currently we use the non-transitional processing option -- see
|
||||
// http://unicode.org/reports/tr46/
|
||||
// To switch to transitional processing, change the value of this flag
|
||||
// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to true
|
||||
// (revert bug 1218179).
|
||||
const bool kIDNA2008_TransitionalProcessing = false;
|
||||
|
||||
#include "ICUUtils.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::unicode;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RFC 1034 - 3.1. Name space specifications and terminology
|
||||
static const uint32_t kMaxDNSNodeLen = 63;
|
||||
// RFC 3490 - 5. ACE prefix
|
||||
static const char kACEPrefix[] = "xn--";
|
||||
#define kACEPrefixLen 4
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define NS_NET_PREF_IDNBLACKLIST "network.IDN.blacklist_chars"
|
||||
#define NS_NET_PREF_SHOWPUNYCODE "network.IDN_show_punycode"
|
||||
#define NS_NET_PREF_IDNWHITELIST "network.IDN.whitelist."
|
||||
#define NS_NET_PREF_IDNUSEWHITELIST "network.IDN.use_whitelist"
|
||||
#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile"
|
||||
|
||||
inline bool isOnlySafeChars(const nsAFlatString& in,
|
||||
const nsAFlatString& blacklist)
|
||||
{
|
||||
return (blacklist.IsEmpty() ||
|
||||
in.FindCharInSet(blacklist) == kNotFound);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIDNService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* Implementation file */
|
||||
NS_IMPL_ISUPPORTS(nsIDNService,
|
||||
nsIIDNService,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsresult nsIDNService::Init()
|
||||
{
|
||||
nsCOMPtr<nsIPrefService> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
|
||||
if (prefs)
|
||||
prefs->GetBranch(NS_NET_PREF_IDNWHITELIST, getter_AddRefs(mIDNWhitelistPrefBranch));
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefInternal(do_QueryInterface(prefs));
|
||||
if (prefInternal) {
|
||||
prefInternal->AddObserver(NS_NET_PREF_IDNBLACKLIST, this, true);
|
||||
prefInternal->AddObserver(NS_NET_PREF_SHOWPUNYCODE, this, true);
|
||||
prefInternal->AddObserver(NS_NET_PREF_IDNRESTRICTION, this, true);
|
||||
prefInternal->AddObserver(NS_NET_PREF_IDNUSEWHITELIST, this, true);
|
||||
prefsChanged(prefInternal, nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIDNService::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(aSubject) );
|
||||
if (prefBranch)
|
||||
prefsChanged(prefBranch, aData);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const char16_t *pref)
|
||||
{
|
||||
if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNBLACKLIST).Equals(pref)) {
|
||||
nsCOMPtr<nsISupportsString> blacklist;
|
||||
nsresult rv = prefBranch->GetComplexValue(NS_NET_PREF_IDNBLACKLIST,
|
||||
NS_GET_IID(nsISupportsString),
|
||||
getter_AddRefs(blacklist));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
blacklist->ToString(getter_Copies(mIDNBlacklist));
|
||||
else
|
||||
mIDNBlacklist.Truncate();
|
||||
}
|
||||
if (!pref || NS_LITERAL_STRING(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) {
|
||||
bool val;
|
||||
if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val)))
|
||||
mShowPunycode = val;
|
||||
}
|
||||
if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNUSEWHITELIST).Equals(pref)) {
|
||||
bool val;
|
||||
if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_IDNUSEWHITELIST,
|
||||
&val)))
|
||||
mIDNUseWhitelist = val;
|
||||
}
|
||||
if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) {
|
||||
nsXPIDLCString profile;
|
||||
if (NS_FAILED(prefBranch->GetCharPref(NS_NET_PREF_IDNRESTRICTION,
|
||||
getter_Copies(profile)))) {
|
||||
profile.Truncate();
|
||||
}
|
||||
if (profile.EqualsLiteral("moderate")) {
|
||||
mRestrictionProfile = eModeratelyRestrictiveProfile;
|
||||
} else if (profile.EqualsLiteral("high")) {
|
||||
mRestrictionProfile = eHighlyRestrictiveProfile;
|
||||
} else {
|
||||
mRestrictionProfile = eASCIIOnlyProfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIDNService::nsIDNService()
|
||||
: mShowPunycode(false)
|
||||
, mIDNUseWhitelist(false)
|
||||
{
|
||||
#ifdef IDNA2008
|
||||
uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ;
|
||||
if (!kIDNA2008_TransitionalProcessing) {
|
||||
IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE;
|
||||
}
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
mIDNA = uidna_openUTS46(IDNAOptions, &errorCode);
|
||||
#else
|
||||
if (idn_success != idn_nameprep_create(nullptr, &mNamePrepHandle))
|
||||
mNamePrepHandle = nullptr;
|
||||
|
||||
mNormalizer = do_GetService(NS_UNICODE_NORMALIZER_CONTRACTID);
|
||||
/* member initializers and constructor code */
|
||||
#endif
|
||||
}
|
||||
|
||||
nsIDNService::~nsIDNService()
|
||||
{
|
||||
#ifdef IDNA2008
|
||||
uidna_close(mIDNA);
|
||||
#else
|
||||
idn_nameprep_destroy(mNamePrepHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IDNA2008
|
||||
nsresult
|
||||
nsIDNService::IDNA2008ToUnicode(const nsACString& input, nsAString& output)
|
||||
{
|
||||
NS_ConvertUTF8toUTF16 inputStr(input);
|
||||
UIDNAInfo info = UIDNA_INFO_INITIALIZER;
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
int32_t inLen = inputStr.Length();
|
||||
int32_t outMaxLen = kMaxDNSNodeLen + 1;
|
||||
UChar outputBuffer[kMaxDNSNodeLen + 1];
|
||||
|
||||
int32_t outLen = uidna_labelToUnicode(mIDNA, (const UChar*)inputStr.get(),
|
||||
inLen, outputBuffer, outMaxLen,
|
||||
&info, &errorCode);
|
||||
if (info.errors != 0) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
|
||||
}
|
||||
|
||||
nsresult rv = ICUUtils::UErrorToNsResult(errorCode);
|
||||
if (rv == NS_ERROR_FAILURE) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIDNService::IDNA2008StringPrep(const nsAString& input,
|
||||
nsAString& output,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
UIDNAInfo info = UIDNA_INFO_INITIALIZER;
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
int32_t inLen = input.Length();
|
||||
int32_t outMaxLen = kMaxDNSNodeLen + 1;
|
||||
UChar outputBuffer[kMaxDNSNodeLen + 1];
|
||||
|
||||
int32_t outLen =
|
||||
uidna_labelToUnicode(mIDNA, (const UChar*)PromiseFlatString(input).get(),
|
||||
inLen, outputBuffer, outMaxLen, &info, &errorCode);
|
||||
nsresult rv = ICUUtils::UErrorToNsResult(errorCode);
|
||||
if (rv == NS_ERROR_FAILURE) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Output the result of nameToUnicode even if there were errors
|
||||
ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
|
||||
|
||||
if (flag == eStringPrepIgnoreErrors) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (info.errors != 0) {
|
||||
if (flag == eStringPrepForDNS) {
|
||||
output.Truncate();
|
||||
}
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace)
|
||||
{
|
||||
return UTF8toACE(input, ace, eStringPrepForDNS);
|
||||
}
|
||||
|
||||
nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
nsresult rv;
|
||||
NS_ConvertUTF8toUTF16 ustr(input);
|
||||
|
||||
// map ideographic period to ASCII period etc.
|
||||
normalizeFullStops(ustr);
|
||||
|
||||
uint32_t len, offset;
|
||||
len = 0;
|
||||
offset = 0;
|
||||
nsAutoCString encodedBuf;
|
||||
|
||||
nsAString::const_iterator start, end;
|
||||
ustr.BeginReading(start);
|
||||
ustr.EndReading(end);
|
||||
ace.Truncate();
|
||||
|
||||
// encode nodes if non ASCII
|
||||
while (start != end) {
|
||||
len++;
|
||||
if (*start++ == (char16_t)'.') {
|
||||
rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, flag);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ace.Append(encodedBuf);
|
||||
ace.Append('.');
|
||||
offset += len;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// encode the last node if non ASCII
|
||||
if (len) {
|
||||
rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, flag);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ace.Append(encodedBuf);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString & input, nsACString & _retval)
|
||||
{
|
||||
return ACEtoUTF8(input, _retval, eStringPrepForDNS);
|
||||
}
|
||||
|
||||
nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
// RFC 3490 - 4.2 ToUnicode
|
||||
// ToUnicode never fails. If any step fails, then the original input
|
||||
// sequence is returned immediately in that step.
|
||||
|
||||
uint32_t len = 0, offset = 0;
|
||||
nsAutoCString decodedBuf;
|
||||
|
||||
nsACString::const_iterator start, end;
|
||||
input.BeginReading(start);
|
||||
input.EndReading(end);
|
||||
_retval.Truncate();
|
||||
|
||||
// loop and decode nodes
|
||||
while (start != end) {
|
||||
len++;
|
||||
if (*start++ == '.') {
|
||||
if (NS_FAILED(decodeACE(Substring(input, offset, len - 1), decodedBuf,
|
||||
flag))) {
|
||||
_retval.Assign(input);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
_retval.Append(decodedBuf);
|
||||
_retval.Append('.');
|
||||
offset += len;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
// decode the last node
|
||||
if (len) {
|
||||
if (NS_FAILED(decodeACE(Substring(input, offset, len), decodedBuf,
|
||||
flag)))
|
||||
_retval.Assign(input);
|
||||
else
|
||||
_retval.Append(decodedBuf);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIDNService::IsACE(const nsACString & input, bool *_retval)
|
||||
{
|
||||
const char *data = input.BeginReading();
|
||||
uint32_t dataLen = input.Length();
|
||||
|
||||
// look for the ACE prefix in the input string. it may occur
|
||||
// at the beginning of any segment in the domain name. for
|
||||
// example: "www.xn--ENCODED.com"
|
||||
|
||||
const char *p = PL_strncasestr(data, kACEPrefix, dataLen);
|
||||
|
||||
*_retval = p && (p == data || *(p - 1) == '.');
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIDNService::Normalize(const nsACString & input,
|
||||
nsACString & output)
|
||||
{
|
||||
// protect against bogus input
|
||||
NS_ENSURE_TRUE(IsUTF8(input), NS_ERROR_UNEXPECTED);
|
||||
|
||||
NS_ConvertUTF8toUTF16 inUTF16(input);
|
||||
normalizeFullStops(inUTF16);
|
||||
|
||||
// pass the domain name to stringprep label by label
|
||||
nsAutoString outUTF16, outLabel;
|
||||
|
||||
uint32_t len = 0, offset = 0;
|
||||
nsresult rv;
|
||||
nsAString::const_iterator start, end;
|
||||
inUTF16.BeginReading(start);
|
||||
inUTF16.EndReading(end);
|
||||
|
||||
while (start != end) {
|
||||
len++;
|
||||
if (*start++ == char16_t('.')) {
|
||||
rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel,
|
||||
eStringPrepIgnoreErrors);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
outUTF16.Append(outLabel);
|
||||
outUTF16.Append(char16_t('.'));
|
||||
offset += len;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
rv = stringPrep(Substring(inUTF16, offset, len), outLabel,
|
||||
eStringPrepIgnoreErrors);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
outUTF16.Append(outLabel);
|
||||
}
|
||||
|
||||
CopyUTF16toUTF8(outUTF16, output);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * _isASCII, nsACString & _retval)
|
||||
{
|
||||
// If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist.
|
||||
// Else, if host is already UTF-8, then make sure it is normalized per IDN.
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Even if the hostname is not ASCII, individual labels may still be ACE, so
|
||||
// test IsACE before testing IsASCII
|
||||
bool isACE;
|
||||
IsACE(input, &isACE);
|
||||
|
||||
if (IsASCII(input)) {
|
||||
// first, canonicalize the host to lowercase, for whitelist lookup
|
||||
_retval = input;
|
||||
ToLowerCase(_retval);
|
||||
|
||||
if (isACE && !mShowPunycode) {
|
||||
// ACEtoUTF8() can't fail, but might return the original ACE string
|
||||
nsAutoCString temp(_retval);
|
||||
// If the domain is in the whitelist, return the host in UTF-8.
|
||||
// Otherwise convert from ACE to UTF8 only those labels which are
|
||||
// considered safe for display
|
||||
ACEtoUTF8(temp, _retval, isInWhitelist(temp) ?
|
||||
eStringPrepIgnoreErrors : eStringPrepForUI);
|
||||
*_isASCII = IsASCII(_retval);
|
||||
} else {
|
||||
*_isASCII = true;
|
||||
}
|
||||
} else {
|
||||
// We have to normalize the hostname before testing against the domain
|
||||
// whitelist (see bug 315411), and to ensure the entire string gets
|
||||
// normalized.
|
||||
//
|
||||
// Normalization and the tests for safe display below, assume that the
|
||||
// input is Unicode, so first convert any ACE labels to UTF8
|
||||
if (isACE) {
|
||||
nsAutoCString temp;
|
||||
ACEtoUTF8(input, temp, eStringPrepIgnoreErrors);
|
||||
rv = Normalize(temp, _retval);
|
||||
} else {
|
||||
rv = Normalize(input, _retval);
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mShowPunycode && NS_SUCCEEDED(UTF8toACE(_retval, _retval,
|
||||
eStringPrepIgnoreErrors))) {
|
||||
*_isASCII = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// normalization could result in an ASCII-only hostname. alternatively, if
|
||||
// the host is converted to ACE by the normalizer, then the host may contain
|
||||
// unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, and bug 309311.
|
||||
*_isASCII = IsASCII(_retval);
|
||||
if (!*_isASCII && !isInWhitelist(_retval)) {
|
||||
// UTF8toACE with eStringPrepForUI may return a domain name where
|
||||
// some labels are in UTF-8 and some are in ACE, depending on
|
||||
// whether they are considered safe for display
|
||||
rv = UTF8toACE(_retval, _retval, eStringPrepForUI);
|
||||
*_isASCII = IsASCII(_retval);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static nsresult utf16ToUcs4(const nsAString& in,
|
||||
uint32_t *out,
|
||||
uint32_t outBufLen,
|
||||
uint32_t *outLen)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
nsAString::const_iterator start, end;
|
||||
in.BeginReading(start);
|
||||
in.EndReading(end);
|
||||
|
||||
while (start != end) {
|
||||
char16_t curChar;
|
||||
|
||||
curChar= *start++;
|
||||
|
||||
if (start != end &&
|
||||
NS_IS_HIGH_SURROGATE(curChar) &&
|
||||
NS_IS_LOW_SURROGATE(*start)) {
|
||||
out[i] = SURROGATE_TO_UCS4(curChar, *start);
|
||||
++start;
|
||||
}
|
||||
else
|
||||
out[i] = curChar;
|
||||
|
||||
i++;
|
||||
if (i >= outBufLen)
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
out[i] = (uint32_t)'\0';
|
||||
*outLen = i;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifndef IDNA2008
|
||||
static void ucs4toUtf16(const uint32_t *in, nsAString& out)
|
||||
{
|
||||
while (*in) {
|
||||
if (!IS_IN_BMP(*in)) {
|
||||
out.Append((char16_t) H_SURROGATE(*in));
|
||||
out.Append((char16_t) L_SURROGATE(*in));
|
||||
}
|
||||
else
|
||||
out.Append((char16_t) *in);
|
||||
in++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static nsresult punycode(const nsAString& in, nsACString& out)
|
||||
{
|
||||
uint32_t ucs4Buf[kMaxDNSNodeLen + 1];
|
||||
uint32_t ucs4Len = 0u;
|
||||
nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// need maximum 20 bits to encode 16 bit Unicode character
|
||||
// (include null terminator)
|
||||
const uint32_t kEncodedBufSize = kMaxDNSNodeLen * 20 / 8 + 1 + 1;
|
||||
char encodedBuf[kEncodedBufSize];
|
||||
punycode_uint encodedLength = kEncodedBufSize;
|
||||
|
||||
enum punycode_status status = punycode_encode(ucs4Len,
|
||||
ucs4Buf,
|
||||
nullptr,
|
||||
&encodedLength,
|
||||
encodedBuf);
|
||||
|
||||
if (punycode_success != status ||
|
||||
encodedLength >= kEncodedBufSize)
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
|
||||
encodedBuf[encodedLength] = '\0';
|
||||
out.Assign(nsDependentCString(kACEPrefix) + nsDependentCString(encodedBuf));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// RFC 3454
|
||||
//
|
||||
// 1) Map -- For each character in the input, check if it has a mapping
|
||||
// and, if so, replace it with its mapping. This is described in section 3.
|
||||
//
|
||||
// 2) Normalize -- Possibly normalize the result of step 1 using Unicode
|
||||
// normalization. This is described in section 4.
|
||||
//
|
||||
// 3) Prohibit -- Check for any characters that are not allowed in the
|
||||
// output. If any are found, return an error. This is described in section
|
||||
// 5.
|
||||
//
|
||||
// 4) Check bidi -- Possibly check for right-to-left characters, and if any
|
||||
// are found, make sure that the whole string satisfies the requirements
|
||||
// for bidirectional strings. If the string does not satisfy the requirements
|
||||
// for bidirectional strings, return an error. This is described in section 6.
|
||||
//
|
||||
// 5) Check unassigned code points -- If allowUnassigned is false, check for
|
||||
// any unassigned Unicode points and if any are found return an error.
|
||||
// This is described in section 7.
|
||||
//
|
||||
nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
#ifdef IDNA2008
|
||||
return IDNA2008StringPrep(in, out, flag);
|
||||
#else
|
||||
if (!mNamePrepHandle || !mNormalizer)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
uint32_t ucs4Buf[kMaxDNSNodeLen + 1];
|
||||
uint32_t ucs4Len;
|
||||
nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// map
|
||||
idn_result_t idn_err;
|
||||
|
||||
uint32_t namePrepBuf[kMaxDNSNodeLen * 3]; // map up to three characters
|
||||
idn_err = idn_nameprep_map(mNamePrepHandle, (const uint32_t *) ucs4Buf,
|
||||
(uint32_t *) namePrepBuf, kMaxDNSNodeLen * 3);
|
||||
NS_ENSURE_TRUE(idn_err == idn_success, NS_ERROR_MALFORMED_URI);
|
||||
|
||||
nsAutoString namePrepStr;
|
||||
ucs4toUtf16(namePrepBuf, namePrepStr);
|
||||
if (namePrepStr.Length() >= kMaxDNSNodeLen)
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
|
||||
// normalize
|
||||
nsAutoString normlizedStr;
|
||||
rv = mNormalizer->NormalizeUnicodeNFKC(namePrepStr, normlizedStr);
|
||||
if (normlizedStr.Length() >= kMaxDNSNodeLen)
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
|
||||
// set the result string
|
||||
out.Assign(normlizedStr);
|
||||
|
||||
if (flag == eStringPrepIgnoreErrors) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// prohibit
|
||||
const uint32_t *found = nullptr;
|
||||
idn_err = idn_nameprep_isprohibited(mNamePrepHandle,
|
||||
(const uint32_t *) ucs4Buf, &found);
|
||||
if (idn_err != idn_success || found) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
} else {
|
||||
// check bidi
|
||||
idn_err = idn_nameprep_isvalidbidi(mNamePrepHandle,
|
||||
(const uint32_t *) ucs4Buf, &found);
|
||||
if (idn_err != idn_success || found) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
} else if (flag == eStringPrepForUI) {
|
||||
// check unassigned code points
|
||||
idn_err = idn_nameprep_isunassigned(mNamePrepHandle,
|
||||
(const uint32_t *) ucs4Buf, &found);
|
||||
if (idn_err != idn_success || found) {
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag == eStringPrepForDNS && NS_FAILED(rv)) {
|
||||
out.Truncate();
|
||||
}
|
||||
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
out.Truncate();
|
||||
|
||||
if (in.Length() > kMaxDNSNodeLen) {
|
||||
NS_WARNING("IDN node too large");
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
if (IsASCII(in)) {
|
||||
LossyCopyUTF16toASCII(in, out);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString strPrep;
|
||||
rv = stringPrep(in, strPrep, flag);
|
||||
if (flag == eStringPrepForDNS) {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (IsASCII(strPrep)) {
|
||||
LossyCopyUTF16toASCII(strPrep, out);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (flag == eStringPrepForUI && NS_SUCCEEDED(rv) && isLabelSafe(in)) {
|
||||
CopyUTF16toUTF8(strPrep, out);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = punycode(strPrep, out);
|
||||
// Check that the encoded output isn't larger than the maximum length
|
||||
// of a DNS node per RFC 1034.
|
||||
// This test isn't necessary in the code paths above where the input
|
||||
// is ASCII (since the output will be the same length as the input) or
|
||||
// where we convert to UTF-8 (since the output is only used for
|
||||
// display in the UI and not passed to DNS and can legitimately be
|
||||
// longer than the limit).
|
||||
if (out.Length() > kMaxDNSNodeLen) {
|
||||
NS_WARNING("IDN node too large");
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// RFC 3490
|
||||
// 1) Whenever dots are used as label separators, the following characters
|
||||
// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
|
||||
// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
|
||||
// stop).
|
||||
|
||||
void nsIDNService::normalizeFullStops(nsAString& s)
|
||||
{
|
||||
nsAString::const_iterator start, end;
|
||||
s.BeginReading(start);
|
||||
s.EndReading(end);
|
||||
int32_t index = 0;
|
||||
|
||||
while (start != end) {
|
||||
switch (*start) {
|
||||
case 0x3002:
|
||||
case 0xFF0E:
|
||||
case 0xFF61:
|
||||
s.Replace(index, 1, NS_LITERAL_STRING("."));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
start++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out,
|
||||
stringPrepFlag flag)
|
||||
{
|
||||
bool isAce;
|
||||
IsACE(in, &isAce);
|
||||
if (!isAce) {
|
||||
out.Assign(in);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString utf16;
|
||||
#ifdef IDNA2008
|
||||
nsresult result = IDNA2008ToUnicode(in, utf16);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
#else
|
||||
// RFC 3490 - 4.2 ToUnicode
|
||||
// The ToUnicode output never contains more code points than its input.
|
||||
punycode_uint output_length = in.Length() - kACEPrefixLen + 1;
|
||||
auto *output = new punycode_uint[output_length];
|
||||
NS_ENSURE_TRUE(output, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
enum punycode_status status = punycode_decode(in.Length() - kACEPrefixLen,
|
||||
PromiseFlatCString(in).get() + kACEPrefixLen,
|
||||
&output_length,
|
||||
output,
|
||||
nullptr);
|
||||
if (status != punycode_success) {
|
||||
delete [] output;
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
// UCS4 -> UTF8
|
||||
output[output_length] = 0;
|
||||
ucs4toUtf16(output, utf16);
|
||||
delete [] output;
|
||||
#endif
|
||||
if (flag != eStringPrepForUI || isLabelSafe(utf16)) {
|
||||
CopyUTF16toUTF8(utf16, out);
|
||||
} else {
|
||||
out.Assign(in);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Validation: encode back to ACE and compare the strings
|
||||
nsAutoCString ace;
|
||||
nsresult rv = UTF8toACE(out, ace, flag);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (flag == eStringPrepForDNS &&
|
||||
!ace.Equals(in, nsCaseInsensitiveCStringComparator())) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool nsIDNService::isInWhitelist(const nsACString &host)
|
||||
{
|
||||
if (mIDNUseWhitelist && mIDNWhitelistPrefBranch) {
|
||||
nsAutoCString tld(host);
|
||||
// make sure the host is ACE for lookup and check that there are no
|
||||
// unassigned codepoints
|
||||
if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, eStringPrepForDNS))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// truncate trailing dots first
|
||||
tld.Trim(".");
|
||||
int32_t pos = tld.RFind(".");
|
||||
if (pos == kNotFound)
|
||||
return false;
|
||||
|
||||
tld.Cut(0, pos + 1);
|
||||
|
||||
bool safe;
|
||||
if (NS_SUCCEEDED(mIDNWhitelistPrefBranch->GetBoolPref(tld.get(), &safe)))
|
||||
return safe;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nsIDNService::isLabelSafe(const nsAString &label)
|
||||
{
|
||||
if (!isOnlySafeChars(PromiseFlatString(label), mIDNBlacklist)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should never get here if the label is ASCII
|
||||
NS_ASSERTION(!IsASCII(label), "ASCII label in IDN checking");
|
||||
if (mRestrictionProfile == eASCIIOnlyProfile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAString::const_iterator current, end;
|
||||
label.BeginReading(current);
|
||||
label.EndReading(end);
|
||||
|
||||
Script lastScript = Script::INVALID;
|
||||
uint32_t previousChar = 0;
|
||||
uint32_t savedNumberingSystem = 0;
|
||||
// Simplified/Traditional Chinese check temporarily disabled -- bug 857481
|
||||
#if 0
|
||||
HanVariantType savedHanVariant = HVT_NotHan;
|
||||
#endif
|
||||
|
||||
int32_t savedScript = -1;
|
||||
|
||||
while (current != end) {
|
||||
uint32_t ch = *current++;
|
||||
|
||||
if (NS_IS_HIGH_SURROGATE(ch) && current != end &&
|
||||
NS_IS_LOW_SURROGATE(*current)) {
|
||||
ch = SURROGATE_TO_UCS4(ch, *current++);
|
||||
}
|
||||
|
||||
// Check for restricted characters; aspirational scripts are NOT permitted,
|
||||
// in anticipation of the category being merged into Limited-Use scripts
|
||||
// in the upcoming (Unicode 10.0-based) revision of UAX #31.
|
||||
XidmodType xm = GetIdentifierModification(ch);
|
||||
if (xm != XIDMOD_RECOMMENDED &&
|
||||
xm != XIDMOD_INCLUSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for mixed script
|
||||
Script script = GetScriptCode(ch);
|
||||
if (script != Script::COMMON &&
|
||||
script != Script::INHERITED &&
|
||||
script != lastScript) {
|
||||
if (illegalScriptCombo(script, savedScript)) {
|
||||
return false;
|
||||
}
|
||||
lastScript = script;
|
||||
}
|
||||
|
||||
// Check for mixed numbering systems
|
||||
if (GetGeneralCategory(ch) ==
|
||||
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) {
|
||||
uint32_t zeroCharacter = ch - GetNumericValue(ch);
|
||||
if (savedNumberingSystem == 0) {
|
||||
// If we encounter a decimal number, save the zero character from that
|
||||
// numbering system.
|
||||
savedNumberingSystem = zeroCharacter;
|
||||
} else if (zeroCharacter != savedNumberingSystem) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for consecutive non-spacing marks
|
||||
if (previousChar != 0 &&
|
||||
previousChar == ch &&
|
||||
GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simplified/Traditional Chinese check temporarily disabled -- bug 857481
|
||||
#if 0
|
||||
|
||||
// Check for both simplified-only and traditional-only Chinese characters
|
||||
HanVariantType hanVariant = GetHanVariant(ch);
|
||||
if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) {
|
||||
if (savedHanVariant == HVT_NotHan) {
|
||||
savedHanVariant = hanVariant;
|
||||
} else if (hanVariant != savedHanVariant) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
previousChar = ch;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Scripts that we care about in illegalScriptCombo
|
||||
static const Script scriptTable[] = {
|
||||
Script::BOPOMOFO, Script::CYRILLIC, Script::GREEK,
|
||||
Script::HANGUL, Script::HAN, Script::HIRAGANA,
|
||||
Script::KATAKANA, Script::LATIN };
|
||||
|
||||
#define BOPO 0
|
||||
#define CYRL 1
|
||||
#define GREK 2
|
||||
#define HANG 3
|
||||
#define HANI 4
|
||||
#define HIRA 5
|
||||
#define KATA 6
|
||||
#define LATN 7
|
||||
#define OTHR 8
|
||||
#define JPAN 9 // Latin + Han + Hiragana + Katakana
|
||||
#define CHNA 10 // Latin + Han + Bopomofo
|
||||
#define KORE 11 // Latin + Han + Hangul
|
||||
#define HNLT 12 // Latin + Han (could be any of the above combinations)
|
||||
#define FAIL 13
|
||||
|
||||
static inline int32_t findScriptIndex(Script aScript)
|
||||
{
|
||||
int32_t tableLength = sizeof(scriptTable) / sizeof(int32_t);
|
||||
for (int32_t index = 0; index < tableLength; ++index) {
|
||||
if (aScript == scriptTable[index]) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return OTHR;
|
||||
}
|
||||
|
||||
static const int32_t scriptComboTable[13][9] = {
|
||||
/* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR
|
||||
* savedScript */
|
||||
/* BOPO */ { BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL },
|
||||
/* CYRL */ { FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL },
|
||||
/* GREK */ { FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL },
|
||||
/* HANG */ { FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL },
|
||||
/* HANI */ { CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL },
|
||||
/* HIRA */ { FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL },
|
||||
/* KATA */ { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL },
|
||||
/* LATN */ { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR },
|
||||
/* OTHR */ { FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL },
|
||||
/* JPAN */ { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL },
|
||||
/* CHNA */ { CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL },
|
||||
/* KORE */ { FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL },
|
||||
/* HNLT */ { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL }
|
||||
};
|
||||
|
||||
bool nsIDNService::illegalScriptCombo(Script script, int32_t& savedScript)
|
||||
{
|
||||
if (savedScript == -1) {
|
||||
savedScript = findScriptIndex(script);
|
||||
return false;
|
||||
}
|
||||
|
||||
savedScript = scriptComboTable[savedScript] [findScriptIndex(script)];
|
||||
/*
|
||||
* Special case combinations that depend on which profile is in use
|
||||
* In the Highly Restrictive profile Latin is not allowed with any
|
||||
* other script
|
||||
*
|
||||
* In the Moderately Restrictive profile Latin mixed with any other
|
||||
* single script is allowed.
|
||||
*/
|
||||
return ((savedScript == OTHR &&
|
||||
mRestrictionProfile == eHighlyRestrictiveProfile) ||
|
||||
savedScript == FAIL);
|
||||
}
|
||||
|
||||
#undef BOPO
|
||||
#undef CYRL
|
||||
#undef GREK
|
||||
#undef HANG
|
||||
#undef HANI
|
||||
#undef HIRA
|
||||
#undef KATA
|
||||
#undef LATN
|
||||
#undef OTHR
|
||||
#undef JPAN
|
||||
#undef CHNA
|
||||
#undef KORE
|
||||
#undef HNLT
|
||||
#undef FAIL
|
||||
195
netwerk/dns/nsIDNService.h
Normal file
195
netwerk/dns/nsIDNService.h
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/* -*- 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 nsIDNService_h__
|
||||
#define nsIDNService_h__
|
||||
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsUnicodeScriptCodes.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#ifdef IDNA2008
|
||||
#include "unicode/uidna.h"
|
||||
#else
|
||||
#include "nsIUnicodeNormalizer.h"
|
||||
#include "nsIDNKitInterface.h"
|
||||
#endif
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIPrefBranch;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIDNService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsIDNService final : public nsIIDNService,
|
||||
public nsIObserver,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIIDNSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsIDNService();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
protected:
|
||||
virtual ~nsIDNService();
|
||||
|
||||
private:
|
||||
enum stringPrepFlag {
|
||||
eStringPrepForDNS,
|
||||
eStringPrepForUI,
|
||||
eStringPrepIgnoreErrors
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the following characters that must be recognized as label
|
||||
* separators per RFC 3490 to ASCII full stop characters
|
||||
*
|
||||
* U+3002 (ideographic full stop)
|
||||
* U+FF0E (fullwidth full stop)
|
||||
* U+FF61 (halfwidth ideographic full stop)
|
||||
*/
|
||||
void normalizeFullStops(nsAString& s);
|
||||
|
||||
/**
|
||||
* Convert and encode a DNS label in ACE/punycode.
|
||||
* @param flag
|
||||
* if eStringPrepIgnoreErrors, all non-ASCII labels are
|
||||
* converted to punycode.
|
||||
* if eStringPrepForUI, only labels that are considered safe
|
||||
* for display are converted.
|
||||
* @see isLabelSafe
|
||||
* if eStringPrepForDNS and stringPrep finds an illegal
|
||||
* character, returns NS_FAILURE and out is empty
|
||||
*/
|
||||
nsresult stringPrepAndACE(const nsAString& in, nsACString& out,
|
||||
stringPrepFlag flag);
|
||||
|
||||
/**
|
||||
* Convert a DNS label using the stringprep profile defined in RFC 3454
|
||||
*/
|
||||
nsresult stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag);
|
||||
|
||||
/**
|
||||
* Decode an ACE-encoded DNS label to UTF-8
|
||||
*
|
||||
* @param flag
|
||||
* if eStringPrepForUI and the label is not considered safe to
|
||||
* display, the output is the same as the input
|
||||
* @see isLabelSafe
|
||||
*/
|
||||
nsresult decodeACE(const nsACString& in, nsACString& out,
|
||||
stringPrepFlag flag);
|
||||
|
||||
/**
|
||||
* Convert complete domain names between UTF8 and ACE and vice versa
|
||||
*
|
||||
* @param flag is passed to decodeACE or stringPrepAndACE for each
|
||||
* label individually, so the output may contain some labels in
|
||||
* punycode and some in UTF-8
|
||||
*/
|
||||
nsresult UTF8toACE(const nsACString& input, nsACString& ace,
|
||||
stringPrepFlag flag);
|
||||
nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval,
|
||||
stringPrepFlag flag);
|
||||
|
||||
bool isInWhitelist(const nsACString &host);
|
||||
void prefsChanged(nsIPrefBranch *prefBranch, const char16_t *pref);
|
||||
|
||||
/**
|
||||
* Determine whether a label is considered safe to display to the user
|
||||
* according to the algorithm defined in UTR 39 and the profile
|
||||
* selected in mRestrictionProfile.
|
||||
*
|
||||
* For the ASCII-only profile, returns false for all labels containing
|
||||
* non-ASCII characters.
|
||||
*
|
||||
* For the other profiles, returns false for labels containing any of
|
||||
* the following:
|
||||
*
|
||||
* Characters in scripts other than the "recommended scripts" and
|
||||
* "aspirational scripts" defined in
|
||||
* http://www.unicode.org/reports/tr31/#Table_Recommended_Scripts
|
||||
* and http://www.unicode.org/reports/tr31/#Aspirational_Use_Scripts
|
||||
* This includes codepoints that are not defined as Unicode
|
||||
* characters
|
||||
*
|
||||
* Illegal combinations of scripts (@see illegalScriptCombo)
|
||||
*
|
||||
* Numbers from more than one different numbering system
|
||||
*
|
||||
* Sequences of the same non-spacing mark
|
||||
*
|
||||
* Both simplified-only and traditional-only Chinese characters
|
||||
* XXX this test was disabled by bug 857481
|
||||
*/
|
||||
bool isLabelSafe(const nsAString &label);
|
||||
|
||||
/**
|
||||
* Determine whether a combination of scripts in a single label is
|
||||
* permitted according to the algorithm defined in UTR 39 and the
|
||||
* profile selected in mRestrictionProfile.
|
||||
*
|
||||
* For the "Highly restrictive" profile, all characters in each
|
||||
* identifier must be from a single script, or from the combinations:
|
||||
* Latin + Han + Hiragana + Katakana;
|
||||
* Latin + Han + Bopomofo; or
|
||||
* Latin + Han + Hangul
|
||||
*
|
||||
* For the "Moderately restrictive" profile, Latin is also allowed
|
||||
* with other scripts except Cyrillic and Greek
|
||||
*/
|
||||
bool illegalScriptCombo(mozilla::unicode::Script script,
|
||||
int32_t& savedScript);
|
||||
|
||||
#ifdef IDNA2008
|
||||
/**
|
||||
* Convert a DNS label from ASCII to Unicode using IDNA2008
|
||||
*/
|
||||
nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output);
|
||||
|
||||
/**
|
||||
* Convert a DNS label to a normalized form conforming to IDNA2008
|
||||
*/
|
||||
nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output,
|
||||
stringPrepFlag flag);
|
||||
|
||||
UIDNA* mIDNA;
|
||||
#else
|
||||
idn_nameprep_t mNamePrepHandle;
|
||||
nsCOMPtr<nsIUnicodeNormalizer> mNormalizer;
|
||||
#endif
|
||||
nsXPIDLString mIDNBlacklist;
|
||||
|
||||
/**
|
||||
* Flag set by the pref network.IDN_show_punycode. When it is true,
|
||||
* IDNs containing non-ASCII characters are always displayed to the
|
||||
* user in punycode
|
||||
*/
|
||||
bool mShowPunycode;
|
||||
|
||||
/**
|
||||
* Restriction-level Detection profiles defined in UTR 39
|
||||
* http://www.unicode.org/reports/tr39/#Restriction_Level_Detection,
|
||||
* and selected by the pref network.IDN.restriction_profile
|
||||
*/
|
||||
enum restrictionProfile {
|
||||
eASCIIOnlyProfile,
|
||||
eHighlyRestrictiveProfile,
|
||||
eModeratelyRestrictiveProfile
|
||||
};
|
||||
restrictionProfile mRestrictionProfile;
|
||||
nsCOMPtr<nsIPrefBranch> mIDNWhitelistPrefBranch;
|
||||
bool mIDNUseWhitelist;
|
||||
};
|
||||
|
||||
#endif // nsIDNService_h__
|
||||
125
netwerk/dns/nsIEffectiveTLDService.idl
Normal file
125
netwerk/dns/nsIEffectiveTLDService.idl
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- 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 "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(68067eb5-ad8d-43cb-a043-1cc85ebe06e7)]
|
||||
interface nsIEffectiveTLDService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns the public suffix of a URI. A public suffix is the highest-level domain
|
||||
* under which individual domains may be registered; it may therefore contain one
|
||||
* or more dots. For example, the public suffix for "www.bbc.co.uk" is "co.uk",
|
||||
* because the .uk TLD does not allow the registration of domains at the
|
||||
* second level ("bbc.uk" is forbidden).
|
||||
*
|
||||
* The public suffix will be returned encoded in ASCII/ACE and will be normalized
|
||||
* according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost().
|
||||
* If consumers wish to compare the result of this method against the host from
|
||||
* another nsIURI, the host should be obtained using nsIURI::GetAsciiHost().
|
||||
* In the case of nested URIs, the innermost URI will be used.
|
||||
*
|
||||
* @param aURI The URI to be analyzed
|
||||
*
|
||||
* @returns the public suffix
|
||||
*
|
||||
* @throws NS_ERROR_UNEXPECTED
|
||||
* or other error returned by nsIIDNService::normalize when
|
||||
* the hostname contains characters disallowed in URIs
|
||||
* @throws NS_ERROR_HOST_IS_IP_ADDRESS
|
||||
* if the host is a numeric IPv4 or IPv6 address (as determined by
|
||||
* the success of a call to PR_StringToNetAddr()).
|
||||
*/
|
||||
ACString getPublicSuffix(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Returns the base domain of a URI; that is, the public suffix with a given
|
||||
* number of additional domain name parts. For example, the result of this method
|
||||
* for "www.bbc.co.uk", depending on the value of aAdditionalParts parameter, will
|
||||
* be:
|
||||
*
|
||||
* 0 (default) -> bbc.co.uk
|
||||
* 1 -> www.bbc.co.uk
|
||||
*
|
||||
* Similarly, the public suffix for "www.developer.mozilla.org" is "org", and the base
|
||||
* domain will be:
|
||||
*
|
||||
* 0 (default) -> mozilla.org
|
||||
* 1 -> developer.mozilla.org
|
||||
* 2 -> www.developer.mozilla.org
|
||||
*
|
||||
* The base domain will be returned encoded in ASCII/ACE and will be normalized
|
||||
* according to RFC 3454, i.e. the same encoding returned by nsIURI::GetAsciiHost().
|
||||
* If consumers wish to compare the result of this method against the host from
|
||||
* another nsIURI, the host should be obtained using nsIURI::GetAsciiHost().
|
||||
* In the case of nested URIs, the innermost URI will be used.
|
||||
*
|
||||
* @param aURI The URI to be analyzed
|
||||
* @param aAdditionalParts Number of domain name parts to be
|
||||
* returned in addition to the public suffix
|
||||
*
|
||||
* @returns the base domain (public suffix plus the requested number of additional parts)
|
||||
*
|
||||
* @throws NS_ERROR_UNEXPECTED
|
||||
* or other error returned by nsIIDNService::normalize when
|
||||
* the hostname contains characters disallowed in URIs
|
||||
* @throws NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
|
||||
* when there are insufficient subdomain levels in the hostname to satisfy the
|
||||
* requested aAdditionalParts value.
|
||||
* @throws NS_ERROR_HOST_IS_IP_ADDRESS
|
||||
* if aHost is a numeric IPv4 or IPv6 address (as determined by
|
||||
* the success of a call to PR_StringToNetAddr()).
|
||||
*
|
||||
* @see getPublicSuffix()
|
||||
*/
|
||||
ACString getBaseDomain(in nsIURI aURI, [optional] in uint32_t aAdditionalParts);
|
||||
|
||||
/**
|
||||
* NOTE: It is strongly recommended to use getPublicSuffix() above if a suitable
|
||||
* nsIURI is available. Only use this method if this is not the case.
|
||||
*
|
||||
* Returns the public suffix of a host string. Otherwise identical to getPublicSuffix().
|
||||
*
|
||||
* @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
|
||||
* port, or path) will cause this method to throw. ASCII/ACE and
|
||||
* UTF8 encodings are acceptable as input; normalization will
|
||||
* be performed as specified in getBaseDomain().
|
||||
*
|
||||
* @see getPublicSuffix()
|
||||
*/
|
||||
ACString getPublicSuffixFromHost(in AUTF8String aHost);
|
||||
|
||||
/**
|
||||
* NOTE: It is strongly recommended to use getBaseDomain() above if a suitable
|
||||
* nsIURI is available. Only use this method if this is not the case.
|
||||
*
|
||||
* Returns the base domain of a host string. Otherwise identical to getBaseDomain().
|
||||
*
|
||||
* @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
|
||||
* port, or path) will cause this method to throw. ASCII/ACE and
|
||||
* UTF8 encodings are acceptable as input; normalization will
|
||||
* be performed as specified in getBaseDomain().
|
||||
*
|
||||
* @see getBaseDomain()
|
||||
*/
|
||||
ACString getBaseDomainFromHost(in AUTF8String aHost, [optional] in uint32_t aAdditionalParts);
|
||||
|
||||
/**
|
||||
* Returns the parent sub-domain of a host string. If the host is a base
|
||||
* domain, it will throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
|
||||
*
|
||||
* For example: "player.bbc.co.uk" would return "bbc.co.uk" and
|
||||
* "bbc.co.uk" would throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
|
||||
*
|
||||
* @param aHost The host to be analyzed. Any additional parts (e.g. scheme,
|
||||
* port, or path) will cause this method to throw. ASCII/ACE and
|
||||
* UTF8 encodings are acceptable as input; normalization will
|
||||
* be performed as specified in getBaseDomain().
|
||||
*/
|
||||
ACString getNextSubDomain(in AUTF8String aHost);
|
||||
};
|
||||
|
||||
58
netwerk/dns/nsIIDNService.idl
Normal file
58
netwerk/dns/nsIIDNService.idl
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* nsIIDNService interface.
|
||||
*
|
||||
* IDN (Internationalized Domain Name) support. Provides facilities
|
||||
* for manipulating IDN hostnames according to the specification set
|
||||
* forth by the IETF.
|
||||
*
|
||||
* IDN effort:
|
||||
* http://www.ietf.org/html.characters/idn-charter.html
|
||||
* http://www.i-dns.net
|
||||
*
|
||||
* IDNA specification:
|
||||
* http://search.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt
|
||||
*/
|
||||
|
||||
[scriptable, uuid(a592a60e-3621-4f19-a318-2bf233cfad3e)]
|
||||
interface nsIIDNService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Prepares the input hostname according to IDNA ToASCII operation,
|
||||
* the input hostname is assumed to be UTF8-encoded.
|
||||
*/
|
||||
ACString convertUTF8toACE(in AUTF8String input);
|
||||
|
||||
|
||||
/**
|
||||
* This is the ToUnicode operation as specified in the IDNA proposal,
|
||||
* with an additional step to encode the result in UTF-8.
|
||||
* It takes an ACE-encoded hostname and performs ToUnicode to it, then
|
||||
* encodes the resulting string into UTF8.
|
||||
*/
|
||||
AUTF8String convertACEtoUTF8(in ACString input);
|
||||
|
||||
/**
|
||||
* Checks if the input string is ACE encoded or not.
|
||||
*/
|
||||
boolean isACE(in ACString input);
|
||||
|
||||
/**
|
||||
* Performs the unicode normalization needed for hostnames in IDN,
|
||||
* for callers that want early normalization.
|
||||
*/
|
||||
AUTF8String normalize(in AUTF8String input);
|
||||
|
||||
/**
|
||||
* Normalizes and converts a host to UTF-8 if the host is in the IDN
|
||||
* whitelist, otherwise converts it to ACE. This is useful for display
|
||||
* purposes and to ensure an encoding consistent with nsIURI::GetHost().
|
||||
* If the result is ASCII or ACE encoded, |isASCII| will be true.
|
||||
*/
|
||||
AUTF8String convertToDisplayIDN(in AUTF8String input, out boolean isASCII);
|
||||
};
|
||||
34
netwerk/dns/nsPIDNSService.idl
Normal file
34
netwerk/dns/nsPIDNSService.idl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "nsIDNSService.idl"
|
||||
|
||||
/**
|
||||
* This is a private interface used by the internals of the networking library.
|
||||
* It will never be frozen. Do not use it in external code.
|
||||
*/
|
||||
[scriptable, uuid(24e598fd-7b1a-436c-9154-14d8b38df8a5)]
|
||||
interface nsPIDNSService : nsIDNSService
|
||||
{
|
||||
/**
|
||||
* called to initialize the DNS service.
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* called to shutdown the DNS service. any pending asynchronous
|
||||
* requests will be canceled, and the local cache of DNS records
|
||||
* will be cleared. NOTE: the operating system may still have
|
||||
* its own cache of DNS records, which would be unaffected by
|
||||
* this method.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Whether or not DNS prefetching (aka RESOLVE_SPECULATE) is enabled
|
||||
*/
|
||||
attribute boolean prefetchEnabled;
|
||||
};
|
||||
121
netwerk/dns/prepare_tlds.py
Normal file
121
netwerk/dns/prepare_tlds.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# 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/.
|
||||
|
||||
import codecs
|
||||
import encodings.idna
|
||||
import re
|
||||
import sys
|
||||
|
||||
"""
|
||||
Processes a file containing effective TLD data. See the following URL for a
|
||||
description of effective TLDs and of the file format that this script
|
||||
processes (although for the latter you're better off just reading this file's
|
||||
short source code).
|
||||
|
||||
http://wiki.mozilla.org/Gecko:Effective_TLD_Service
|
||||
"""
|
||||
|
||||
def getEffectiveTLDs(path):
|
||||
file = codecs.open(path, "r", "UTF-8")
|
||||
entries = []
|
||||
domains = set()
|
||||
for line in file:
|
||||
# line always contains a line terminator unless the file is empty
|
||||
if len(line) == 0:
|
||||
raise StopIteration
|
||||
line = line.rstrip()
|
||||
# comment, empty, or superfluous line for explicitness purposes
|
||||
if line.startswith("//") or "." not in line:
|
||||
continue
|
||||
line = re.split(r"[ \t\n]", line, 1)[0]
|
||||
entry = EffectiveTLDEntry(line)
|
||||
domain = entry.domain()
|
||||
assert domain not in domains, \
|
||||
"repeating domain %s makes no sense" % domain
|
||||
domains.add(domain)
|
||||
entries.append(entry)
|
||||
|
||||
# Sort the entries so we can use binary search on them.
|
||||
entries.sort(key=EffectiveTLDEntry.domain)
|
||||
|
||||
return entries
|
||||
|
||||
def _normalizeHostname(domain):
|
||||
"""
|
||||
Normalizes the given domain, component by component. ASCII components are
|
||||
lowercased, while non-ASCII components are processed using the ToASCII
|
||||
algorithm.
|
||||
"""
|
||||
def convertLabel(label):
|
||||
if _isASCII(label):
|
||||
return label.lower()
|
||||
return encodings.idna.ToASCII(label)
|
||||
return ".".join(map(convertLabel, domain.split(".")))
|
||||
|
||||
def _isASCII(s):
|
||||
"True if s consists entirely of ASCII characters, false otherwise."
|
||||
for c in s:
|
||||
if ord(c) > 127:
|
||||
return False
|
||||
return True
|
||||
|
||||
class EffectiveTLDEntry:
|
||||
"""
|
||||
Stores an entry in an effective-TLD name file.
|
||||
"""
|
||||
|
||||
_exception = False
|
||||
_wild = False
|
||||
|
||||
def __init__(self, line):
|
||||
"""
|
||||
Creates a TLD entry from a line of data, which must have been stripped of
|
||||
the line ending.
|
||||
"""
|
||||
if line.startswith("!"):
|
||||
self._exception = True
|
||||
domain = line[1:]
|
||||
elif line.startswith("*."):
|
||||
self._wild = True
|
||||
domain = line[2:]
|
||||
else:
|
||||
domain = line
|
||||
self._domain = _normalizeHostname(domain)
|
||||
|
||||
def domain(self):
|
||||
"The domain this represents."
|
||||
return self._domain
|
||||
|
||||
def exception(self):
|
||||
"True if this entry's domain denotes does not denote an effective TLD."
|
||||
return self._exception
|
||||
|
||||
def wild(self):
|
||||
"True if this entry represents a class of effective TLDs."
|
||||
return self._wild
|
||||
|
||||
|
||||
#################
|
||||
# DO EVERYTHING #
|
||||
#################
|
||||
|
||||
def main(output, effective_tld_filename):
|
||||
"""
|
||||
effective_tld_filename is the effective TLD file to parse.
|
||||
A C++ array of { domain, exception, wild } entries representing the
|
||||
eTLD file is then printed to output.
|
||||
"""
|
||||
|
||||
def boolStr(b):
|
||||
if b:
|
||||
return "true"
|
||||
return "false"
|
||||
|
||||
for etld in getEffectiveTLDs(effective_tld_filename):
|
||||
exception = boolStr(etld.exception())
|
||||
wild = boolStr(etld.wild())
|
||||
output.write('ETLD_ENTRY("%s", %s, %s)\n' % (etld.domain(), exception, wild))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.stdout, sys.argv[1])
|
||||
289
netwerk/dns/punycode.c
Normal file
289
netwerk/dns/punycode.c
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
punycode.c from RFC 3492
|
||||
http://www.nicemice.net/idn/
|
||||
Adam M. Costello
|
||||
http://www.nicemice.net/amc/
|
||||
|
||||
This is ANSI C code (C89) implementing Punycode (RFC 3492).
|
||||
|
||||
|
||||
C. Disclaimer and license
|
||||
|
||||
Regarding this entire document or any portion of it (including
|
||||
the pseudocode and C code), the author makes no guarantees and
|
||||
is not responsible for any damage resulting from its use. The
|
||||
author grants irrevocable permission to anyone to use, modify,
|
||||
and distribute it in any way that does not diminish the rights
|
||||
of anyone else to use, modify, and distribute it, provided that
|
||||
redistributed derivative works do not contain misleading author or
|
||||
version information. Derivative works need not be licensed under
|
||||
similar terms.
|
||||
*/
|
||||
|
||||
#include "punycode.h"
|
||||
|
||||
/**********************************************************/
|
||||
/* Implementation (would normally go in its own .c file): */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*** Bootstring parameters for Punycode ***/
|
||||
|
||||
enum { base = 36, tmin = 1, tmax = 26, skew = 38, damp = 700,
|
||||
initial_bias = 72, initial_n = 0x80, delimiter = 0x2D };
|
||||
|
||||
/* basic(cp) tests whether cp is a basic code point: */
|
||||
#define basic(cp) ((punycode_uint)(cp) < 0x80)
|
||||
|
||||
/* delim(cp) tests whether cp is a delimiter: */
|
||||
#define delim(cp) ((cp) == delimiter)
|
||||
|
||||
/* decode_digit(cp) returns the numeric value of a basic code */
|
||||
/* point (for use in representing integers) in the range 0 to */
|
||||
/* base-1, or base if cp is does not represent a value. */
|
||||
|
||||
static punycode_uint decode_digit(punycode_uint cp)
|
||||
{
|
||||
return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 :
|
||||
cp - 97 < 26 ? cp - 97 : base;
|
||||
}
|
||||
|
||||
/* encode_digit(d,flag) returns the basic code point whose value */
|
||||
/* (when used for representing integers) is d, which needs to be in */
|
||||
/* the range 0 to base-1. The lowercase form is used unless flag is */
|
||||
/* nonzero, in which case the uppercase form is used. The behavior */
|
||||
/* is undefined if flag is nonzero and digit d has no uppercase form. */
|
||||
|
||||
static char encode_digit(punycode_uint d, int flag)
|
||||
{
|
||||
return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
|
||||
/* 0..25 map to ASCII a..z or A..Z */
|
||||
/* 26..35 map to ASCII 0..9 */
|
||||
}
|
||||
|
||||
/* flagged(bcp) tests whether a basic code point is flagged */
|
||||
/* (uppercase). The behavior is undefined if bcp is not a */
|
||||
/* basic code point. */
|
||||
|
||||
#define flagged(bcp) ((punycode_uint)(bcp) - 65 < 26)
|
||||
|
||||
/* encode_basic(bcp,flag) forces a basic code point to lowercase */
|
||||
/* if flag is zero, uppercase if flag is nonzero, and returns */
|
||||
/* the resulting code point. The code point is unchanged if it */
|
||||
/* is caseless. The behavior is undefined if bcp is not a basic */
|
||||
/* code point. */
|
||||
|
||||
static char encode_basic(punycode_uint bcp, int flag)
|
||||
{
|
||||
bcp -= (bcp - 97 < 26) << 5;
|
||||
return bcp + ((!flag && (bcp - 65 < 26)) << 5);
|
||||
}
|
||||
|
||||
/*** Platform-specific constants ***/
|
||||
|
||||
/* maxint is the maximum value of a punycode_uint variable: */
|
||||
static const punycode_uint maxint = (punycode_uint) -1;
|
||||
/* Because maxint is unsigned, -1 becomes the maximum value. */
|
||||
|
||||
/*** Bias adaptation function ***/
|
||||
|
||||
static punycode_uint adapt(
|
||||
punycode_uint delta, punycode_uint numpoints, int firsttime )
|
||||
{
|
||||
punycode_uint k;
|
||||
|
||||
delta = firsttime ? delta / damp : delta >> 1;
|
||||
/* delta >> 1 is a faster way of doing delta / 2 */
|
||||
delta += delta / numpoints;
|
||||
|
||||
for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
|
||||
delta /= base - tmin;
|
||||
}
|
||||
|
||||
return k + (base - tmin + 1) * delta / (delta + skew);
|
||||
}
|
||||
|
||||
/*** Main encode function ***/
|
||||
|
||||
enum punycode_status punycode_encode(
|
||||
punycode_uint input_length,
|
||||
const punycode_uint input[],
|
||||
const unsigned char case_flags[],
|
||||
punycode_uint *output_length,
|
||||
char output[] )
|
||||
{
|
||||
punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t;
|
||||
|
||||
/* Initialize the state: */
|
||||
|
||||
n = initial_n;
|
||||
delta = out = 0;
|
||||
max_out = *output_length;
|
||||
bias = initial_bias;
|
||||
|
||||
/* Handle the basic code points: */
|
||||
|
||||
for (j = 0; j < input_length; ++j) {
|
||||
if (basic(input[j])) {
|
||||
if (max_out - out < 2) return punycode_big_output;
|
||||
output[out++] =
|
||||
case_flags ? encode_basic(input[j], case_flags[j]) : (char)input[j];
|
||||
}
|
||||
/* else if (input[j] < n) return punycode_bad_input; */
|
||||
/* (not needed for Punycode with unsigned code points) */
|
||||
}
|
||||
|
||||
h = b = out;
|
||||
|
||||
/* h is the number of code points that have been handled, b is the */
|
||||
/* number of basic code points, and out is the number of characters */
|
||||
/* that have been output. */
|
||||
|
||||
if (b > 0) output[out++] = delimiter;
|
||||
|
||||
/* Main encoding loop: */
|
||||
|
||||
while (h < input_length) {
|
||||
/* All non-basic code points < n have been */
|
||||
/* handled already. Find the next larger one: */
|
||||
|
||||
for (m = maxint, j = 0; j < input_length; ++j) {
|
||||
/* if (basic(input[j])) continue; */
|
||||
/* (not needed for Punycode) */
|
||||
if (input[j] >= n && input[j] < m) m = input[j];
|
||||
}
|
||||
|
||||
/* Increase delta enough to advance the decoder's */
|
||||
/* <n,i> state to <m,0>, but guard against overflow: */
|
||||
|
||||
if (m - n > (maxint - delta) / (h + 1)) return punycode_overflow;
|
||||
delta += (m - n) * (h + 1);
|
||||
n = m;
|
||||
|
||||
for (j = 0; j < input_length; ++j) {
|
||||
/* Punycode does not need to check whether input[j] is basic: */
|
||||
if (input[j] < n /* || basic(input[j]) */ ) {
|
||||
if (++delta == 0) return punycode_overflow;
|
||||
}
|
||||
|
||||
if (input[j] == n) {
|
||||
/* Represent delta as a generalized variable-length integer: */
|
||||
|
||||
for (q = delta, k = base; ; k += base) {
|
||||
if (out >= max_out) return punycode_big_output;
|
||||
t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
|
||||
k >= bias + tmax ? tmax : k - bias;
|
||||
if (q < t) break;
|
||||
output[out++] = encode_digit(t + (q - t) % (base - t), 0);
|
||||
q = (q - t) / (base - t);
|
||||
}
|
||||
|
||||
output[out++] = encode_digit(q, case_flags && case_flags[j]);
|
||||
bias = adapt(delta, h + 1, h == b);
|
||||
delta = 0;
|
||||
++h;
|
||||
}
|
||||
}
|
||||
|
||||
++delta, ++n;
|
||||
}
|
||||
|
||||
*output_length = out;
|
||||
return punycode_success;
|
||||
}
|
||||
|
||||
/*** Main decode function ***/
|
||||
|
||||
enum punycode_status punycode_decode(
|
||||
punycode_uint input_length,
|
||||
const char input[],
|
||||
punycode_uint *output_length,
|
||||
punycode_uint output[],
|
||||
unsigned char case_flags[] )
|
||||
{
|
||||
punycode_uint n, out, i, max_out, bias,
|
||||
b, j, in, oldi, w, k, digit, t;
|
||||
|
||||
if (!input_length) {
|
||||
return punycode_bad_input;
|
||||
}
|
||||
|
||||
/* Initialize the state: */
|
||||
|
||||
n = initial_n;
|
||||
out = i = 0;
|
||||
max_out = *output_length;
|
||||
bias = initial_bias;
|
||||
|
||||
/* Handle the basic code points: Let b be the number of input code */
|
||||
/* points before the last delimiter, or 0 if there is none, then */
|
||||
/* copy the first b code points to the output. */
|
||||
|
||||
for (b = 0, j = input_length - 1 ; j > 0; --j) {
|
||||
if (delim(input[j])) {
|
||||
b = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (b > max_out) return punycode_big_output;
|
||||
|
||||
for (j = 0; j < b; ++j) {
|
||||
if (case_flags) case_flags[out] = flagged(input[j]);
|
||||
if (!basic(input[j])) return punycode_bad_input;
|
||||
output[out++] = input[j];
|
||||
}
|
||||
|
||||
/* Main decoding loop: Start just after the last delimiter if any */
|
||||
/* basic code points were copied; start at the beginning otherwise. */
|
||||
|
||||
for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
|
||||
|
||||
/* in is the index of the next character to be consumed, and */
|
||||
/* out is the number of code points in the output array. */
|
||||
|
||||
/* Decode a generalized variable-length integer into delta, */
|
||||
/* which gets added to i. The overflow checking is easier */
|
||||
/* if we increase i as we go, then subtract off its starting */
|
||||
/* value at the end to obtain delta. */
|
||||
|
||||
for (oldi = i, w = 1, k = base; ; k += base) {
|
||||
if (in >= input_length) return punycode_bad_input;
|
||||
digit = decode_digit(input[in++]);
|
||||
if (digit >= base) return punycode_bad_input;
|
||||
if (digit > (maxint - i) / w) return punycode_overflow;
|
||||
i += digit * w;
|
||||
t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
|
||||
k >= bias + tmax ? tmax : k - bias;
|
||||
if (digit < t) break;
|
||||
if (w > maxint / (base - t)) return punycode_overflow;
|
||||
w *= (base - t);
|
||||
}
|
||||
|
||||
bias = adapt(i - oldi, out + 1, oldi == 0);
|
||||
|
||||
/* i was supposed to wrap around from out+1 to 0, */
|
||||
/* incrementing n each time, so we'll fix that now: */
|
||||
|
||||
if (i / (out + 1) > maxint - n) return punycode_overflow;
|
||||
n += i / (out + 1);
|
||||
i %= (out + 1);
|
||||
|
||||
/* Insert n at position i of the output: */
|
||||
|
||||
/* not needed for Punycode: */
|
||||
/* if (decode_digit(n) <= base) return punycode_invalid_input; */
|
||||
if (out >= max_out) return punycode_big_output;
|
||||
|
||||
if (case_flags) {
|
||||
memmove(case_flags + i + 1, case_flags + i, out - i);
|
||||
/* Case of last character determines uppercase flag: */
|
||||
case_flags[i] = flagged(input[in - 1]);
|
||||
}
|
||||
|
||||
memmove(output + i + 1, output + i, (out - i) * sizeof *output);
|
||||
output[i++] = n;
|
||||
}
|
||||
|
||||
*output_length = out;
|
||||
return punycode_success;
|
||||
}
|
||||
108
netwerk/dns/punycode.h
Normal file
108
netwerk/dns/punycode.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
punycode.c from RFC 3492
|
||||
http://www.nicemice.net/idn/
|
||||
Adam M. Costello
|
||||
http://www.nicemice.net/amc/
|
||||
|
||||
This is ANSI C code (C89) implementing Punycode (RFC 3492).
|
||||
|
||||
|
||||
|
||||
C. Disclaimer and license
|
||||
|
||||
Regarding this entire document or any portion of it (including
|
||||
the pseudocode and C code), the author makes no guarantees and
|
||||
is not responsible for any damage resulting from its use. The
|
||||
author grants irrevocable permission to anyone to use, modify,
|
||||
and distribute it in any way that does not diminish the rights
|
||||
of anyone else to use, modify, and distribute it, provided that
|
||||
redistributed derivative works do not contain misleading author or
|
||||
version information. Derivative works need not be licensed under
|
||||
similar terms.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/************************************************************/
|
||||
/* Public interface (would normally go in its own .h file): */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
enum punycode_status {
|
||||
punycode_success,
|
||||
punycode_bad_input, /* Input is invalid. */
|
||||
punycode_big_output, /* Output would exceed the space provided. */
|
||||
punycode_overflow /* Input needs wider integers to process. */
|
||||
};
|
||||
|
||||
#if UINT_MAX >= (1 << 26) - 1
|
||||
typedef unsigned int punycode_uint;
|
||||
#else
|
||||
typedef unsigned long punycode_uint;
|
||||
#endif
|
||||
|
||||
enum punycode_status punycode_encode(
|
||||
punycode_uint input_length,
|
||||
const punycode_uint input[],
|
||||
const unsigned char case_flags[],
|
||||
punycode_uint *output_length,
|
||||
char output[] );
|
||||
|
||||
/* punycode_encode() converts Unicode to Punycode. The input */
|
||||
/* is represented as an array of Unicode code points (not code */
|
||||
/* units; surrogate pairs are not allowed), and the output */
|
||||
/* will be represented as an array of ASCII code points. The */
|
||||
/* output string is *not* null-terminated; it will contain */
|
||||
/* zeros if and only if the input contains zeros. (Of course */
|
||||
/* the caller can leave room for a terminator and add one if */
|
||||
/* needed.) The input_length is the number of code points in */
|
||||
/* the input. The output_length is an in/out argument: the */
|
||||
/* caller passes in the maximum number of code points that it */
|
||||
/* can receive, and on successful return it will contain the */
|
||||
/* number of code points actually output. The case_flags array */
|
||||
/* holds input_length boolean values, where nonzero suggests that */
|
||||
/* the corresponding Unicode character be forced to uppercase */
|
||||
/* after being decoded (if possible), and zero suggests that */
|
||||
/* it be forced to lowercase (if possible). ASCII code points */
|
||||
/* are encoded literally, except that ASCII letters are forced */
|
||||
/* to uppercase or lowercase according to the corresponding */
|
||||
/* uppercase flags. If case_flags is a null pointer then ASCII */
|
||||
/* letters are left as they are, and other code points are */
|
||||
/* treated as if their uppercase flags were zero. The return */
|
||||
/* value can be any of the punycode_status values defined above */
|
||||
/* except punycode_bad_input; if not punycode_success, then */
|
||||
/* output_size and output might contain garbage. */
|
||||
|
||||
enum punycode_status punycode_decode(
|
||||
punycode_uint input_length,
|
||||
const char input[],
|
||||
punycode_uint *output_length,
|
||||
punycode_uint output[],
|
||||
unsigned char case_flags[] );
|
||||
|
||||
/* punycode_decode() converts Punycode to Unicode. The input is */
|
||||
/* represented as an array of ASCII code points, and the output */
|
||||
/* will be represented as an array of Unicode code points. The */
|
||||
/* input_length is the number of code points in the input. The */
|
||||
/* output_length is an in/out argument: the caller passes in */
|
||||
/* the maximum number of code points that it can receive, and */
|
||||
/* on successful return it will contain the actual number of */
|
||||
/* code points output. The case_flags array needs room for at */
|
||||
/* least output_length values, or it can be a null pointer if the */
|
||||
/* case information is not needed. A nonzero flag suggests that */
|
||||
/* the corresponding Unicode character be forced to uppercase */
|
||||
/* by the caller (if possible), while zero suggests that it be */
|
||||
/* forced to lowercase (if possible). ASCII code points are */
|
||||
/* output already in the proper case, but their flags will be set */
|
||||
/* appropriately so that applying the flags would be harmless. */
|
||||
/* The return value can be any of the punycode_status values */
|
||||
/* defined above; if not punycode_success, then output_length, */
|
||||
/* output, and case_flags might contain garbage. On success, the */
|
||||
/* decoder will never need to write an output_length greater than */
|
||||
/* input_length, because of how the encoding is defined. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
Loading…
Add table
Add a link
Reference in a new issue