Issue #1829 - Revert "Issue #1751"

This commit is contained in:
Brian Smith 2022-04-26 11:34:08 -05:00 • committed by roytam1
commit 13fcc4a046
949 changed files with 95662 additions and 444 deletions

View file

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; 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 <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <netdb.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/ScopeExit.h"
#include "NetworkInfoServiceImpl.h"
namespace mozilla {
namespace net {
static nsresult
ListInterfaceAddresses(int aFd, const char* aIface, AddrMapType& aAddrMap);
nsresult
DoListAddresses(AddrMapType& aAddrMap)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
return NS_ERROR_FAILURE;
}
auto autoCloseSocket = MakeScopeExit([&] {
close(fd);
});
struct ifconf ifconf;
/* 16k of space should be enough to list all interfaces. Worst case, if it's
* not then we will error out and fail to list addresses. This should only
* happen on pathological machines with way too many interfaces.
*/
char buf[16384];
ifconf.ifc_len = sizeof(buf);
ifconf.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0) {
return NS_ERROR_FAILURE;
}
struct ifreq* ifreq = ifconf.ifc_req;
int i = 0;
while (i < ifconf.ifc_len) {
size_t len = IFNAMSIZ + ifreq->ifr_addr.sa_len;
DebugOnly<nsresult> rv =
ListInterfaceAddresses(fd, ifreq->ifr_name, aAddrMap);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ListInterfaceAddresses failed");
ifreq = (struct ifreq*) ((char*)ifreq + len);
i += len;
}
autoCloseSocket.release();
return NS_OK;
}
static nsresult
ListInterfaceAddresses(int aFd, const char* aInterface, AddrMapType& aAddrMap)
{
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(struct ifreq));
strncpy(ifreq.ifr_name, aInterface, IFNAMSIZ - 1);
if (ioctl(aFd, SIOCGIFADDR, &ifreq) != 0) {
return NS_ERROR_FAILURE;
}
char host[128];
int family;
switch(family=ifreq.ifr_addr.sa_family) {
case AF_INET:
case AF_INET6:
getnameinfo(&ifreq.ifr_addr, sizeof(ifreq.ifr_addr), host, sizeof(host), 0, 0, NI_NUMERICHOST);
break;
case AF_UNSPEC:
return NS_OK;
default:
// Unknown family.
return NS_OK;
}
nsCString ifaceStr;
ifaceStr.AssignASCII(aInterface);
nsCString addrStr;
addrStr.AssignASCII(host);
aAddrMap.Put(ifaceStr, addrStr);
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -257,6 +257,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'nsURLHelperWin.cpp',
'ShutdownLayer.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'nsURLHelperOSX.cpp',
]
else:
SOURCES += [
'nsURLHelperUnix.cpp',
@ -268,6 +272,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'NetworkInfoServiceWindows.cpp',
'nsNetworkInfoService.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'NetworkInfoServiceCocoa.cpp',
'nsNetworkInfoService.cpp',
]
elif CONFIG['OS_ARCH'] == 'Linux':
SOURCES += [
'NetworkInfoServiceLinux.cpp',

View file

@ -3055,6 +3055,17 @@ nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
}
return NS_OK;
#elif defined(XP_DARWIN)
// Darwin uses sec; only supports idle time being set.
int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
&aIdleTime, sizeof(aIdleTime));
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
#elif defined(XP_UNIX)
// Not all *nix OSes support the following setsockopt() options
// build errors will tell us if they are not.

View file

@ -0,0 +1,216 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=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/. */
/* Mac OS X-specific local file uri parsing */
#include "nsURLHelper.h"
#include "nsEscape.h"
#include "nsIFile.h"
#include "nsTArray.h"
#include "nsReadableUtils.h"
#include <Carbon/Carbon.h>
static nsTArray<nsCString> *gVolumeList = nullptr;
static bool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent)
{
// Return whether the 1st path component in path (escaped) is equal to the name
// of a mounted volume. Return the 1st path component (unescaped) in any case.
// This needs to be done as quickly as possible, so we cache a list of volume names.
// XXX Register an event handler to detect drives being mounted/unmounted?
if (!gVolumeList) {
gVolumeList = new nsTArray<nsCString>;
if (!gVolumeList) {
return false; // out of memory
}
}
// Cache a list of volume names
if (!gVolumeList->Length()) {
OSErr err;
ItemCount volumeIndex = 1;
do {
HFSUniStr255 volName;
FSRef rootDirectory;
err = ::FSGetVolumeInfo(0, volumeIndex, nullptr, kFSVolInfoNone, nullptr,
&volName, &rootDirectory);
if (err == noErr) {
NS_ConvertUTF16toUTF8 volNameStr(Substring((char16_t *)volName.unicode,
(char16_t *)volName.unicode + volName.length));
gVolumeList->AppendElement(volNameStr);
volumeIndex++;
}
} while (err == noErr);
}
// Extract the first component of the path
nsACString::const_iterator start;
path.BeginReading(start);
start.advance(1); // path begins with '/'
nsACString::const_iterator directory_end;
path.EndReading(directory_end);
nsACString::const_iterator component_end(start);
FindCharInReadable('/', component_end, directory_end);
nsAutoCString flatComponent((Substring(start, component_end)));
NS_UnescapeURL(flatComponent);
int32_t foundIndex = gVolumeList->IndexOf(flatComponent);
firstPathComponent = flatComponent;
return (foundIndex != -1);
}
void
net_ShutdownURLHelperOSX()
{
delete gVolumeList;
gVolumeList = nullptr;
}
static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath)
{
// Use CFURL to do the conversion. We don't want to do this by simply
// using SwapSlashColon - we need the charset mapped from MacRoman
// to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change)
// prepended if the path is not on the boot drive.
CFStringRef pathStrRef = CFStringCreateWithCString(nullptr,
PromiseFlatCString(hfsPath).get(),
kCFStringEncodingMacRoman);
if (!pathStrRef)
return NS_ERROR_FAILURE;
nsresult rv = NS_ERROR_FAILURE;
CFURLRef urlRef = CFURLCreateWithFileSystemPath(nullptr,
pathStrRef, kCFURLHFSPathStyle, true);
if (urlRef) {
UInt8 pathBuf[PATH_MAX];
if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) {
posixPath = (char *)pathBuf;
rv = NS_OK;
}
}
CFRelease(pathStrRef);
if (urlRef)
CFRelease(urlRef);
return rv;
}
static void SwapSlashColon(char *s)
{
while (*s) {
if (*s == '/')
*s = ':';
else if (*s == ':')
*s = '/';
s++;
}
}
nsresult
net_GetURLSpecFromActualFile(nsIFile *aFile, nsACString &result)
{
// NOTE: This is identical to the implementation in nsURLHelperUnix.cpp
nsresult rv;
nsAutoCString ePath;
// construct URL spec from native file path
rv = aFile->GetNativePath(ePath);
if (NS_FAILED(rv))
return rv;
nsAutoCString escPath;
NS_NAMED_LITERAL_CSTRING(prefix, "file://");
// Escape the path with the directory mask
if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath))
escPath.Insert(prefix, 0);
else
escPath.Assign(prefix + ePath);
// esc_Directory does not escape the semicolons, so if a filename
// contains semicolons we need to manually escape them.
// This replacement should be removed in bug #473280
escPath.ReplaceSubstring(";", "%3b");
result = escPath;
return NS_OK;
}
nsresult
net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result)
{
// NOTE: See also the implementation in nsURLHelperUnix.cpp
// This matches it except for the HFS path handling.
nsresult rv;
nsCOMPtr<nsIFile> localFile;
rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localFile));
if (NS_FAILED(rv))
return rv;
nsAutoCString directory, fileBaseName, fileExtension, path;
bool bHFSPath = false;
rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension);
if (NS_FAILED(rv))
return rv;
if (!directory.IsEmpty()) {
NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path);
// The canonical form of file URLs on OSX use POSIX paths:
// file:///path-name.
// But, we still encounter file URLs that use HFS paths:
// file:///volume-name/path-name
// Determine that here and normalize HFS paths to POSIX.
nsAutoCString possibleVolName;
if (pathBeginsWithVolName(directory, possibleVolName)) {
// Though we know it begins with a volume name, it could still
// be a valid POSIX path if the boot drive is named "Mac HD"
// and there is a directory "Mac HD" at its root. If such a
// directory doesn't exist, we'll assume this is an HFS path.
FSRef testRef;
possibleVolName.Insert("/", 0);
if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nullptr) != noErr)
bHFSPath = true;
}
if (bHFSPath) {
// "%2F"s need to become slashes, while all other slashes need to
// become colons. If we start out by changing "%2F"s to colons, we
// can reply on SwapSlashColon() to do what we need
path.ReplaceSubstring("%2F", ":");
path.Cut(0, 1); // directory begins with '/'
SwapSlashColon((char *)path.get());
// At this point, path is an HFS path made using the same
// algorithm as nsURLHelperMac. We'll convert to POSIX below.
}
}
if (!fileBaseName.IsEmpty())
NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path);
if (!fileExtension.IsEmpty()) {
path += '.';
NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path);
}
NS_UnescapeURL(path);
if (path.Length() != strlen(path.get()))
return NS_ERROR_FILE_INVALID_PATH;
if (bHFSPath)
convertHFSPathtoPOSIX(path, path);
// assuming path is encoded in the native charset
rv = localFile->InitWithNativePath(path);
if (NS_FAILED(rv))
return rv;
localFile.forget(result);
return NS_OK;
}

View file

@ -36,7 +36,12 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'/netwerk/system/win32',
]
if CONFIG['OS_ARCH'] == 'Linux':
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
LOCAL_INCLUDES += [
'/netwerk/system/mac',
]
elif CONFIG['OS_ARCH'] == 'Linux':
LOCAL_INCLUDES += [
'/netwerk/system/linux',
]

View 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

View 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

View 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

View 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

View file

@ -3,20 +3,32 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'nsDNSServiceDiscovery.js',
'nsDNSServiceDiscovery.manifest',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'MDNSResponderOperator.cpp',
'MDNSResponderReply.cpp',
'nsDNSServiceDiscovery.cpp',
]
EXTRA_JS_MODULES += [
'fallback/DataReader.jsm',
'fallback/DataWriter.jsm',
'fallback/DNSPacket.jsm',
'fallback/DNSRecord.jsm',
'fallback/DNSResourceRecord.jsm',
'fallback/DNSTypes.jsm',
'fallback/MulticastDNS.jsm',
]
LOCAL_INCLUDES += [
'/netwerk/base',
]
else:
EXTRA_COMPONENTS += [
'nsDNSServiceDiscovery.js',
'nsDNSServiceDiscovery.manifest',
]
EXTRA_JS_MODULES += [
'fallback/DataReader.jsm',
'fallback/DataWriter.jsm',
'fallback/DNSPacket.jsm',
'fallback/DNSRecord.jsm',
'fallback/DNSResourceRecord.jsm',
'fallback/DNSTypes.jsm',
'fallback/MulticastDNS.jsm',
]
SOURCES += [
'nsDNSServiceInfo.cpp',

View file

@ -0,0 +1,262 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDNSServiceDiscovery.h"
#include "MDNSResponderOperator.h"
#include "nsICancelable.h"
#include "nsXULAppAPI.h"
#include "private/pprio.h"
namespace mozilla {
namespace net {
namespace {
inline void
StartService()
{
/*** STUB ***/
}
inline void
StopService()
{
/*** STUB ***/
}
class ServiceCounter
{
public:
static bool IsServiceRunning()
{
return !!sUseCount;
}
private:
static uint32_t sUseCount;
protected:
ServiceCounter()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sUseCount++) {
StartService();
}
}
virtual ~ServiceCounter()
{
MOZ_ASSERT(NS_IsMainThread());
if (!--sUseCount) {
StopService();
}
}
};
uint32_t ServiceCounter::sUseCount = 0;
class DiscoveryRequest final : public nsICancelable
, private ServiceCounter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICANCELABLE
explicit DiscoveryRequest(nsDNSServiceDiscovery* aService,
nsIDNSServiceDiscoveryListener* aListener);
private:
virtual ~DiscoveryRequest() { Cancel(NS_OK); }
RefPtr<nsDNSServiceDiscovery> mService;
nsIDNSServiceDiscoveryListener* mListener;
};
NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable)
DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService,
nsIDNSServiceDiscoveryListener* aListener)
: mService(aService)
, mListener(aListener)
{
}
NS_IMETHODIMP
DiscoveryRequest::Cancel(nsresult aReason)
{
if (mService) {
mService->StopDiscovery(mListener);
}
mService = nullptr;
return NS_OK;
}
class RegisterRequest final : public nsICancelable
, private ServiceCounter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICANCELABLE
explicit RegisterRequest(nsDNSServiceDiscovery* aService,
nsIDNSRegistrationListener* aListener);
private:
virtual ~RegisterRequest() { Cancel(NS_OK); }
RefPtr<nsDNSServiceDiscovery> mService;
nsIDNSRegistrationListener* mListener;
};
NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable)
RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService,
nsIDNSRegistrationListener* aListener)
: mService(aService)
, mListener(aListener)
{
}
NS_IMETHODIMP
RegisterRequest::Cancel(nsresult aReason)
{
if (mService) {
mService->UnregisterService(mListener);
}
mService = nullptr;
return NS_OK;
}
} // namespace anonymous
NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery)
nsDNSServiceDiscovery::~nsDNSServiceDiscovery()
{
}
nsresult
nsDNSServiceDiscovery::Init()
{
if (!XRE_IsParentProcess()) {
MOZ_ASSERT(false, "nsDNSServiceDiscovery can only be used in parent process");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType,
nsIDNSServiceDiscoveryListener* aListener,
nsICancelable** aRetVal)
{
MOZ_ASSERT(aRetVal);
nsresult rv;
if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) {
return rv;
}
nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener);
RefPtr<BrowseOperator> browserOp = new BrowseOperator(aServiceType,
aListener);
if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) {
return rv;
}
mDiscoveryMap.Put(aListener, browserOp);
req.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
nsDNSServiceDiscovery::StopDiscovery(nsIDNSServiceDiscoveryListener* aListener)
{
nsresult rv;
RefPtr<BrowseOperator> browserOp;
if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) {
return NS_OK;
}
browserOp->Cancel(); // cancel non-started operation
if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) {
return rv;
}
mDiscoveryMap.Remove(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo,
nsIDNSRegistrationListener* aListener,
nsICancelable** aRetVal)
{
MOZ_ASSERT(aRetVal);
nsresult rv;
if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) {
return rv;
}
nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener);
RefPtr<RegisterOperator> registerOp = new RegisterOperator(aServiceInfo,
aListener);
if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) {
return rv;
}
mRegisterMap.Put(aListener, registerOp);
req.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
nsDNSServiceDiscovery::UnregisterService(nsIDNSRegistrationListener* aListener)
{
nsresult rv;
RefPtr<RegisterOperator> registerOp;
if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) {
return NS_OK;
}
registerOp->Cancel(); // cancel non-started operation
if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) {
return rv;
}
mRegisterMap.Remove(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo,
nsIDNSServiceResolveListener* aListener)
{
if (!ServiceCounter::IsServiceRunning()) {
return NS_ERROR_FAILURE;
}
nsresult rv;
RefPtr<ResolveOperator> resolveOp = new ResolveOperator(aServiceInfo,
aListener);
if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) {
return rv;
}
return NS_OK;
}
} // namespace net
} // namespace mozilla

View file

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
#include "MDNSResponderOperator.h"
#include "nsIDNSServiceDiscovery.h"
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
#include "nsRefPtrHashtable.h"
namespace mozilla {
namespace net {
class BrowseOperator;
class RegisterOperator;
class nsDNSServiceDiscovery final : public nsIDNSServiceDiscovery
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDNSSERVICEDISCOVERY
explicit nsDNSServiceDiscovery() = default;
/*
** The mDNS service is started on demand. If no one uses, mDNS service will not
** start. Therefore, all operations before service started will fail
** and get error code |kDNSServiceErr_ServiceNotRunning| defined in dns_sd.h.
**/
nsresult Init();
nsresult StopDiscovery(nsIDNSServiceDiscoveryListener* aListener);
nsresult UnregisterService(nsIDNSRegistrationListener* aListener);
private:
virtual ~nsDNSServiceDiscovery();
nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap;
nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h

View file

@ -11,7 +11,6 @@ XPIDL_MODULE = 'necko_http'
SOURCES += [
'mozTXTToHTMLConv.cpp',
'nsBinHexDecoder.cpp',
'nsDirIndex.cpp',
'nsDirIndexParser.cpp',
'nsHTTPCompressConv.cpp',
@ -27,6 +26,11 @@ if 'ftp' in CONFIG['NECKO_PROTOCOLS']:
'ParseFTPList.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'cocoa':
SOURCES += [
'nsBinHexDecoder.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [

View file

@ -0,0 +1,13 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# 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/.
SOURCES += [
'nsNetworkLinkService.mm',
]
FINAL_LIBRARY = 'xul'
if CONFIG['CLANG_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,54 @@
/* 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 NSNETWORKLINKSERVICEMAC_H_
#define NSNETWORKLINKSERVICEMAC_H_
#include "nsINetworkLinkService.h"
#include "nsIObserver.h"
#include <SystemConfiguration/SCNetworkReachability.h>
#include <SystemConfiguration/SystemConfiguration.h>
class nsNetworkLinkService : public nsINetworkLinkService,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETWORKLINKSERVICE
NS_DECL_NSIOBSERVER
nsNetworkLinkService();
nsresult Init();
nsresult Shutdown();
protected:
virtual ~nsNetworkLinkService();
private:
bool mLinkUp;
bool mStatusKnown;
// Toggles allowing the sending of network-changed event.
bool mAllowChangedEvent;
SCNetworkReachabilityRef mReachability;
CFRunLoopRef mCFRunLoop;
CFRunLoopSourceRef mRunLoopSource;
SCDynamicStoreRef mStoreRef;
void UpdateReachability();
void SendEvent(bool aNetworkChanged);
static void ReachabilityChanged(SCNetworkReachabilityRef target,
SCNetworkConnectionFlags flags,
void *info);
static void IPConfigChanged(SCDynamicStoreRef store,
CFArrayRef changedKeys,
void *info);
void calculateNetworkId(void);
nsCString mNetworkId;
};
#endif /* NSNETWORKLINKSERVICEMAC_H_ */

View file

@ -0,0 +1,526 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include "nsCOMPtr.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsCRT.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/SHA1.h"
#include "mozilla/Base64.h"
#include "nsNetworkLinkService.h"
#import <Cocoa/Cocoa.h>
#import <netinet/in.h>
#define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
using namespace mozilla;
static LazyLogModule gNotifyAddrLog("nsNotifyAddr");
#define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
// If non-successful, extract the error code and return it. This
// error code dance is inspired by
// http://developer.apple.com/technotes/tn/tn1145.html
static OSStatus getErrorCodeBool(Boolean success)
{
OSStatus err = noErr;
if (!success) {
int scErr = ::SCError();
if (scErr == kSCStatusOK) {
scErr = kSCStatusFailed;
}
err = scErr;
}
return err;
}
// If given a NULL pointer, return the error code.
static OSStatus getErrorCodePtr(const void *value)
{
return getErrorCodeBool(value != NULL);
}
// Convenience function to allow NULL input.
static void CFReleaseSafe(CFTypeRef cf)
{
if (cf) {
// "If cf is NULL, this will cause a runtime error and your
// application will crash." / Apple docs
::CFRelease(cf);
}
}
NS_IMPL_ISUPPORTS(nsNetworkLinkService,
nsINetworkLinkService,
nsIObserver)
nsNetworkLinkService::nsNetworkLinkService()
: mLinkUp(true)
, mStatusKnown(false)
, mAllowChangedEvent(true)
, mReachability(nullptr)
, mCFRunLoop(nullptr)
, mRunLoopSource(nullptr)
, mStoreRef(nullptr)
{
}
nsNetworkLinkService::~nsNetworkLinkService() = default;
NS_IMETHODIMP
nsNetworkLinkService::GetIsLinkUp(bool *aIsUp)
{
*aIsUp = mLinkUp;
return NS_OK;
}
NS_IMETHODIMP
nsNetworkLinkService::GetLinkStatusKnown(bool *aIsUp)
{
*aIsUp = mStatusKnown;
return NS_OK;
}
NS_IMETHODIMP
nsNetworkLinkService::GetLinkType(uint32_t *aLinkType)
{
NS_ENSURE_ARG_POINTER(aLinkType);
// XXX This function has not yet been implemented for this platform
*aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
return NS_OK;
}
#ifndef SA_SIZE
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(uint32_t) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) )
#endif
static char *getMac(struct sockaddr_dl *sdl, char *buf, size_t bufsize)
{
char *cp;
int n, p = 0;
buf[0] = 0;
cp = (char *)LLADDR(sdl);
n = sdl->sdl_alen;
if (n > 0) {
while (--n >= 0) {
p += snprintf(&buf[p], bufsize - p, "%02x%s",
*cp++ & 0xff, n > 0 ? ":" : "");
}
}
return buf;
}
/* If the IP matches, get the MAC and return true */
static bool matchIp(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr,
char *ip, char *buf, size_t bufsize)
{
if (sdl->sdl_alen) {
if (!strcmp(inet_ntoa(addr->sin_addr), ip)) {
getMac(sdl, buf, bufsize);
return true; /* done! */
}
}
return false; /* continue */
}
/*
* Scan for the 'IP' address in the ARP table and store the corresponding MAC
* address in 'mac'. The output buffer is 'maclen' bytes big.
*
* Returns 'true' if it found the IP and returns a MAC.
*/
static bool scanArp(char *ip, char *mac, size_t maclen)
{
int mib[6];
char *lim, *next;
int st;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
mib[5] = RTF_LLINFO;
size_t needed;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
return false;
}
if (needed == 0) {
// empty table
return false;
}
UniquePtr <char[]>buf(new char[needed]);
for (;;) {
st = sysctl(mib, 6, &buf[0], &needed, NULL, 0);
if (st == 0 || errno != ENOMEM) {
break;
}
needed += needed / 8;
auto tmp = MakeUnique<char[]>(needed);
memcpy(&tmp[0], &buf[0], needed);
buf = Move(tmp);
}
if (st == -1) {
return false;
}
lim = &buf[needed];
struct rt_msghdr *rtm;
for (next = &buf[0]; next < lim; next += rtm->rtm_msglen) {
rtm = reinterpret_cast<struct rt_msghdr *>(next);
struct sockaddr_inarp *sin2 =
reinterpret_cast<struct sockaddr_inarp *>(rtm + 1);
struct sockaddr_dl *sdl =
reinterpret_cast<struct sockaddr_dl *>
((char *)sin2 + SA_SIZE(sin2));
if (matchIp(sdl, sin2, ip, mac, maclen)) {
return true;
}
}
return false;
}
static int routingTable(char *gw, size_t aGwLen)
{
size_t needed;
int mib[6];
struct rt_msghdr *rtm;
struct sockaddr *sa;
struct sockaddr_in *sockin;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_DUMP;
mib[5] = 0;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
return 1;
}
UniquePtr <char[]>buf(new char[needed]);
if (sysctl(mib, 6, &buf[0], &needed, NULL, 0) < 0) {
return 3;
}
rtm = reinterpret_cast<struct rt_msghdr *>(&buf[0]);
sa = reinterpret_cast<struct sockaddr *>(rtm + 1);
sa = reinterpret_cast<struct sockaddr *>(SA_SIZE(sa) + (char *)sa);
sockin = reinterpret_cast<struct sockaddr_in *>(sa);
inet_ntop(AF_INET, &sockin->sin_addr.s_addr, gw, aGwLen-1);
return 0;
}
//
// Figure out the current "network identification" string.
//
// It detects the IP of the default gateway in the routing table, then the MAC
// address of that IP in the ARP table before it hashes that string (to avoid
// information leakage).
//
void nsNetworkLinkService::calculateNetworkId(void)
{
bool found = false;
char hw[MAXHOSTNAMELEN];
if (!routingTable(hw, sizeof(hw))) {
char mac[256]; // big enough for a printable MAC address
if (scanArp(hw, mac, sizeof(mac))) {
LOG(("networkid: MAC %s\n", hw));
nsAutoCString mac(hw);
// This 'addition' could potentially be a
// fixed number from the profile or something.
nsAutoCString addition("local-rubbish");
nsAutoCString output;
SHA1Sum sha1;
nsCString combined(mac + addition);
sha1.update(combined.get(), combined.Length());
uint8_t digest[SHA1Sum::kHashSize];
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest),
SHA1Sum::kHashSize);
nsresult rv = Base64Encode(newString, output);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
LOG(("networkid: id %s\n", output.get()));
if (mNetworkId != output) {
// new id
mNetworkId = output;
}
else {
// same id
}
found = true;
}
}
if (!found) {
// no id
}
}
NS_IMETHODIMP
nsNetworkLinkService::Observe(nsISupports *subject,
const char *topic,
const char16_t *data)
{
if (!strcmp(topic, "xpcom-shutdown")) {
Shutdown();
}
return NS_OK;
}
/* static */
void
nsNetworkLinkService::IPConfigChanged(SCDynamicStoreRef aStoreREf,
CFArrayRef aChangedKeys,
void *aInfo)
{
nsNetworkLinkService *service =
static_cast<nsNetworkLinkService*>(aInfo);
service->SendEvent(true);
}
nsresult
nsNetworkLinkService::Init(void)
{
nsresult rv;
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->AddObserver(this, "xpcom-shutdown", false);
NS_ENSURE_SUCCESS(rv, rv);
Preferences::AddBoolVarCache(&mAllowChangedEvent,
NETWORK_NOTIFY_CHANGED_PREF, true);
// If the network reachability API can reach 0.0.0.0 without
// requiring a connection, there is a network interface available.
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
mReachability =
::SCNetworkReachabilityCreateWithAddress(NULL,
(struct sockaddr *)&addr);
if (!mReachability) {
return NS_ERROR_NOT_AVAILABLE;
}
SCNetworkReachabilityContext context = {0, this, NULL, NULL, NULL};
if (!::SCNetworkReachabilitySetCallback(mReachability,
ReachabilityChanged,
&context)) {
NS_WARNING("SCNetworkReachabilitySetCallback failed.");
::CFRelease(mReachability);
mReachability = NULL;
return NS_ERROR_NOT_AVAILABLE;
}
SCDynamicStoreContext storeContext = {0, this, NULL, NULL, NULL};
mStoreRef =
::SCDynamicStoreCreate(NULL,
CFSTR("AddIPAddressListChangeCallbackSCF"),
IPConfigChanged, &storeContext);
CFStringRef patterns[2] = {NULL, NULL};
OSStatus err = getErrorCodePtr(mStoreRef);
if (err == noErr) {
// This pattern is "State:/Network/Service/[^/]+/IPv4".
patterns[0] =
::SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4);
err = getErrorCodePtr(patterns[0]);
if (err == noErr) {
// This pattern is "State:/Network/Service/[^/]+/IPv6".
patterns[1] =
::SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv6);
err = getErrorCodePtr(patterns[1]);
}
}
CFArrayRef patternList = NULL;
// Create a pattern list containing just one pattern,
// then tell SCF that we want to watch changes in keys
// that match that pattern list, then create our run loop
// source.
if (err == noErr) {
patternList = ::CFArrayCreate(NULL, (const void **) patterns,
2, &kCFTypeArrayCallBacks);
if (!patternList) {
err = -1;
}
}
if (err == noErr) {
err =
getErrorCodeBool(::SCDynamicStoreSetNotificationKeys(mStoreRef,
NULL,
patternList));
}
if (err == noErr) {
mRunLoopSource =
::SCDynamicStoreCreateRunLoopSource(NULL, mStoreRef, 0);
err = getErrorCodePtr(mRunLoopSource);
}
CFReleaseSafe(patterns[0]);
CFReleaseSafe(patterns[1]);
CFReleaseSafe(patternList);
if (err != noErr) {
CFReleaseSafe(mStoreRef);
return NS_ERROR_NOT_AVAILABLE;
}
// Get the current run loop. This service is initialized at startup,
// so we shouldn't run in to any problems with modal dialog run loops.
mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
if (!mCFRunLoop) {
NS_WARNING("Could not get current run loop.");
::CFRelease(mReachability);
mReachability = NULL;
return NS_ERROR_NOT_AVAILABLE;
}
::CFRetain(mCFRunLoop);
::CFRunLoopAddSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode);
if (!::SCNetworkReachabilityScheduleWithRunLoop(mReachability, mCFRunLoop,
kCFRunLoopDefaultMode)) {
NS_WARNING("SCNetworkReachabilityScheduleWIthRunLoop failed.");
::CFRelease(mReachability);
mReachability = NULL;
::CFRelease(mCFRunLoop);
mCFRunLoop = NULL;
return NS_ERROR_NOT_AVAILABLE;
}
UpdateReachability();
return NS_OK;
}
nsresult
nsNetworkLinkService::Shutdown()
{
if (!::SCNetworkReachabilityUnscheduleFromRunLoop(mReachability,
mCFRunLoop,
kCFRunLoopDefaultMode)) {
NS_WARNING("SCNetworkReachabilityUnscheduleFromRunLoop failed.");
}
CFRunLoopRemoveSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode);
::CFRelease(mReachability);
mReachability = nullptr;
::CFRelease(mCFRunLoop);
mCFRunLoop = nullptr;
::CFRelease(mStoreRef);
mStoreRef = nullptr;
::CFRelease(mRunLoopSource);
mRunLoopSource = nullptr;
return NS_OK;
}
void
nsNetworkLinkService::UpdateReachability()
{
if (!mReachability) {
return;
}
SCNetworkConnectionFlags flags;
if (!::SCNetworkReachabilityGetFlags(mReachability, &flags)) {
mStatusKnown = false;
return;
}
bool reachable = (flags & kSCNetworkFlagsReachable) != 0;
bool needsConnection = (flags & kSCNetworkFlagsConnectionRequired) != 0;
mLinkUp = (reachable && !needsConnection);
mStatusKnown = true;
}
void
nsNetworkLinkService::SendEvent(bool aNetworkChanged)
{
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (!observerService) {
return;
}
const char *event;
if (aNetworkChanged) {
if (!mAllowChangedEvent) {
return;
}
event = NS_NETWORK_LINK_DATA_CHANGED;
} else if (!mStatusKnown) {
event = NS_NETWORK_LINK_DATA_UNKNOWN;
} else {
event = mLinkUp ? NS_NETWORK_LINK_DATA_UP
: NS_NETWORK_LINK_DATA_DOWN;
}
LOG(("SendEvent: network is '%s'\n", event));
observerService->NotifyObservers(static_cast<nsINetworkLinkService*>(this),
NS_NETWORK_LINK_TOPIC,
NS_ConvertASCIItoUTF16(event).get());
}
/* static */
void
nsNetworkLinkService::ReachabilityChanged(SCNetworkReachabilityRef target,
SCNetworkConnectionFlags flags,
void *info)
{
nsNetworkLinkService *service =
static_cast<nsNetworkLinkService*>(info);
service->UpdateReachability();
service->SendEvent(false);
service->calculateNetworkId();
}

View file

@ -5,5 +5,7 @@
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['win32']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
DIRS += ['mac']
elif CONFIG['OS_ARCH'] == 'Linux':
DIRS += ['linux']