Bug 1115480 - Part 1: Implement XPCOM module for mDNSProvider. r=mcmanus
authorLiang-Heng Chen <xeonchen@mozilla.com>
Wed, 20 May 2015 23:06:00 -0400
changeset 277696 dda118cb0bcc60bcc66ee42f4bae5d5338f46d33
parent 277695 bdc2ca02b0804f61585d5dfb64cfc5d776636a55
child 277697 7ada5429c80deeedcefbd2925355d3f6c80e1bc6
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1115480
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1115480 - Part 1: Implement XPCOM module for mDNSProvider. r=mcmanus
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
config/system-headers
configure.in
mobile/android/installer/package-manifest.in
netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
netwerk/dns/mdns/libmdns/MDNSResponderReply.h
netwerk/dns/mdns/libmdns/moz.build
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
netwerk/dns/mdns/moz.build
netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
netwerk/dns/moz.build
toolkit/library/moz.build
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -276,16 +276,17 @@
 @RESPATH@/components/necko_about.xpt
 @RESPATH@/components/necko_cache.xpt
 @RESPATH@/components/necko_cache2.xpt
 @RESPATH@/components/necko_cookie.xpt
 @RESPATH@/components/necko_dns.xpt
 @RESPATH@/components/necko_file.xpt
 @RESPATH@/components/necko_ftp.xpt
 @RESPATH@/components/necko_http.xpt
+@RESPATH@/components/necko_mdns.xpt
 @RESPATH@/components/necko_res.xpt
 @RESPATH@/components/necko_socket.xpt
 @RESPATH@/components/necko_strconv.xpt
 @RESPATH@/components/necko_viewsource.xpt
 @RESPATH@/components/necko_websocket.xpt
 @RESPATH@/components/necko_wifi.xpt
 @RESPATH@/components/necko_wyciwyg.xpt
 #ifdef MOZ_RTSP
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -281,16 +281,17 @@
 @RESPATH@/components/necko_about.xpt
 @RESPATH@/components/necko_cache.xpt
 @RESPATH@/components/necko_cache2.xpt
 @RESPATH@/components/necko_cookie.xpt
 @RESPATH@/components/necko_dns.xpt
 @RESPATH@/components/necko_file.xpt
 @RESPATH@/components/necko_ftp.xpt
 @RESPATH@/components/necko_http.xpt
+@RESPATH@/components/necko_mdns.xpt
 @RESPATH@/components/necko_res.xpt
 @RESPATH@/components/necko_socket.xpt
 @RESPATH@/components/necko_strconv.xpt
 @RESPATH@/components/necko_viewsource.xpt
 @RESPATH@/components/necko_websocket.xpt
 #ifdef NECKO_WIFI
 @RESPATH@/components/necko_wifi.xpt
 #endif
--- a/config/system-headers
+++ b/config/system-headers
@@ -390,16 +390,19 @@ descrip.h
 Devices.h
 Dialogs.h
 direct.h
 dirent.h
 DiskInit.h
 dlfcn.h
 dlgs.h
 dl.h
+#ifdef MOZ_WIDGET_GONK
+dns_sd.h
+#endif
 docobj.h
 dos/dosextens.h
 dos.h
 Drag.h
 DriverServices.h
 DriverSynchronization.h
 DropInPanel.h
 dvidef.h
--- a/configure.in
+++ b/configure.in
@@ -222,17 +222,17 @@ if test -n "$gonkdir" ; then
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_SUBST(MOZ_OMX_DECODER)
         MOZ_RTSP=1
         MOZ_FMP4=1
         MOZ_SECUREELEMENT=1
         ;;
     17|18)
-        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
+        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include -I$gonkdir/external/mdnsresponder/mDNSShared"
         if test -d "$gonkdir/external/bluetooth/bluez"; then
           GONK_INCLUDES="$GONK_INCLUDES -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
             MOZ_B2G_BT=1
             MOZ_B2G_BT_BLUEZ=1
         elif test -d "$gonkdir/external/bluetooth/bluedroid"; then
             MOZ_B2G_BT=1
             MOZ_B2G_BT_BLUEDROID=1
             if test -d "$gonkdir/system/bluetoothd"; then
@@ -247,17 +247,17 @@ if test -n "$gonkdir" ; then
         AC_SUBST(MOZ_OMX_DECODER)
         MOZ_OMX_ENCODER=1
         AC_SUBST(MOZ_OMX_ENCODER)
         AC_DEFINE(MOZ_OMX_ENCODER)
 	MOZ_FMP4=1
         MOZ_SECUREELEMENT=1
         ;;
     19)
-        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
+        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include -I$gonkdir/external/mdnsresponder/mDNSShared"
         MOZ_B2G_CAMERA=1
         MOZ_B2G_BT=1
         MOZ_B2G_BT_BLUEDROID=1
         if test -d "$gonkdir/system/bluetoothd"; then
             MOZ_B2G_BT_DAEMON=1
         fi
         MOZ_NFC=1
         MOZ_RTSP=1
@@ -266,17 +266,17 @@ if test -n "$gonkdir" ; then
         AC_DEFINE(MOZ_OMX_ENCODER)
         MOZ_AUDIO_OFFLOAD=1
         MOZ_SECUREELEMENT=1
         AC_SUBST(MOZ_AUDIO_OFFLOAD)
         AC_DEFINE(MOZ_AUDIO_OFFLOAD)
         MOZ_FMP4=1
         ;;
     21|22)
-        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
+        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include -I$gonkdir/external/mdnsresponder/mDNSShared"
         MOZ_AUDIO_OFFLOAD=1
         MOZ_OMX_DECODER=1
         MOZ_OMX_ENCODER=1
         AC_DEFINE(MOZ_OMX_ENCODER)
         AC_SUBST(MOZ_AUDIO_OFFLOAD)
         AC_DEFINE(MOZ_AUDIO_OFFLOAD)
         MOZ_FMP4=
         MOZ_B2G_CAMERA=1
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -208,16 +208,17 @@
 @BINPATH@/components/necko_about.xpt
 @BINPATH@/components/necko_cache.xpt
 @BINPATH@/components/necko_cache2.xpt
 @BINPATH@/components/necko_cookie.xpt
 @BINPATH@/components/necko_dns.xpt
 @BINPATH@/components/necko_file.xpt
 @BINPATH@/components/necko_ftp.xpt
 @BINPATH@/components/necko_http.xpt
+@BINPATH@/components/necko_mdns.xpt
 @BINPATH@/components/necko_res.xpt
 @BINPATH@/components/necko_socket.xpt
 @BINPATH@/components/necko_strconv.xpt
 @BINPATH@/components/necko_viewsource.xpt
 @BINPATH@/components/necko_websocket.xpt
 @BINPATH@/components/necko_wifi.xpt
 @BINPATH@/components/necko_wyciwyg.xpt
 @BINPATH@/components/necko.xpt
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
@@ -0,0 +1,665 @@
+/* -*- 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/Endian.h"
+#include "mozilla/Logging.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 "nsNetCID.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCID.h"
+#include "private/pprio.h"
+
+#include "nsASocketHandler.h"
+
+inline PRLogModuleInfo*
+GetOperatorLog()
+{
+  static PRLogModuleInfo* log = PR_NewLogModule("MDNSResponderOperator");
+  return log;
+}
+#undef LOG_I
+#define LOG_I(...) PR_LOG(GetOperatorLog(), PR_LOG_NOTICE, (__VA_ARGS__))
+#undef LOG_E
+#define LOG_E(...) PR_LOG(GetOperatorLog(), PR_LOG_ERROR, (__VA_ARGS__))
+
+namespace mozilla {
+namespace net {
+
+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(fd == mFD);
+
+    if (!mFD) {
+      return;
+    }
+
+    PR_Close(mFD);
+    mFD = nullptr;
+  }
+
+  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)
+    : mSts(nullptr)
+    , mService(aService)
+    , mFD(nullptr)
+    , mAttached(false)
+  {
+    if (!gSocketTransportService)
+    {
+      nsCOMPtr<nsISocketTransportService> sts =
+        do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+    }
+  }
+
+  nsresult Init()
+  {
+    MOZ_ASSERT(PR_GetCurrentThread() != gSocketThread);
+
+    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 (mService) {
+      DNSServiceRefDeallocate(mService);
+      mService = nullptr;
+    }
+
+    if (!gSocketTransportService) {
+      return;
+    }
+
+    PostEvent(&ServiceWatcher::OnMsgClose);
+  }
+
+private:
+  ~ServiceWatcher() = default;
+
+  nsresult PostEvent(void(ServiceWatcher::*func)(void))
+  {
+    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, func);
+    return gSocketTransportService->Dispatch(ev, 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 =
+        NS_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;
+  }
+
+  nsRefPtr<nsSocketTransportService> mSts;
+  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()
+{
+  mThread = nullptr;
+  return ResetService(nullptr);
+}
+
+nsresult
+MDNSResponderOperator::ResetService(DNSServiceRef aService)
+{
+  nsresult rv;
+
+  if (aService != mService) {
+    if (mWatcher) {
+      mWatcher->Close();
+      mWatcher = nullptr;
+    }
+
+    if (aService) {
+      nsRefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService);
+      if (NS_WARN_IF(NS_FAILED(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_WARN_IF(kDNSServiceErr_NoError != err);
+
+  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_TRUE(NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(element))));
+      nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
+      MOZ_ASSERT(property);
+
+      nsAutoString name;
+      nsCOMPtr<nsIVariant> value;
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(property->GetName(name)));
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(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_WARN_IF(kDNSServiceErr_NoError != err);
+
+  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) {
+    mListener->OnServiceRegistered(info);
+  } else {
+    mListener->OnRegistrationFailed(info, aErrorCode);
+  }
+}
+
+ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+                                 nsIDNSServiceResolveListener* aListener)
+  : MDNSResponderOperator()
+  , mServiceInfo(aServiceInfo)
+  , mListener(aListener)
+  , mDeleteProtector()
+{
+}
+
+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;
+  }
+
+  mDeleteProtector = this;
+  return ResetService(service);
+}
+
+nsresult
+ResolveOperator::Stop()
+{
+  nsresult rv = MDNSResponderOperator::Stop();
+  return rv;
+}
+
+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());
+
+  mDeleteProtector = nullptr;
+
+  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 = nullptr;
+  if (count) {
+    attributes = new nsHashPropertyBag();
+    if (NS_WARN_IF(!attributes)) { return; }
+    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) {
+    mListener->OnServiceResolved(info);
+  } else {
+    mListener->OnResolveFailed(info, aErrorCode);
+  }
+
+  NS_WARN_IF(NS_FAILED(Stop()));
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
@@ -0,0 +1,139 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsIDNSServiceDiscovery.h"
+#include "nsIThread.h"
+#include "nsRefPtr.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class 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;
+  nsRefPtr<ServiceWatcher> mWatcher;
+  nsCOMPtr<nsIThread> mThread; // remember caller thread for callback
+  Atomic<bool> mIsCancelled;
+};
+
+class BrowseOperator final : private MDNSResponderOperator
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BrowseOperator)
+
+  BrowseOperator(const nsACString& aServiceType,
+                 nsIDNSServiceDiscoveryListener* aListener);
+
+  nsresult Start() override;
+  nsresult Stop() override;
+  using MDNSResponderOperator::Cancel;
+  using MDNSResponderOperator::GetThread;
+
+  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 : private MDNSResponderOperator
+{
+  enum { TXT_BUFFER_SIZE = 256 };
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RegisterOperator)
+
+  RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
+                   nsIDNSRegistrationListener* aListener);
+
+  nsresult Start() override;
+  nsresult Stop() override;
+  using MDNSResponderOperator::Cancel;
+  using MDNSResponderOperator::GetThread;
+
+  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 : private MDNSResponderOperator
+{
+  enum { TXT_BUFFER_SIZE = 256 };
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ResolveOperator)
+
+  ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+                  nsIDNSServiceResolveListener* aListener);
+
+  nsresult Start() override;
+  nsresult Stop() override;
+  using MDNSResponderOperator::GetThread;
+
+  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;
+
+  nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+  nsCOMPtr<nsIDNSServiceResolveListener> mListener;
+
+  // hold self until callback is made.
+  nsRefPtr<ResolveOperator> mDeleteProtector;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
@@ -0,0 +1,225 @@
+/* -*- 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/Endian.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);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h
@@ -0,0 +1,128 @@
+/* -*- 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 "nsRefPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+class BrowseReplyRunnable final : public nsRunnable
+{
+public:
+  BrowseReplyRunnable(DNSServiceRef aSdRef,
+                      DNSServiceFlags aFlags,
+                      uint32_t aInterfaceIndex,
+                      DNSServiceErrorType aErrorCode,
+                      const nsACString& aServiceName,
+                      const nsACString& aRegType,
+                      const nsACString& aReplyDomain,
+                      BrowseOperator* aContext);
+
+  NS_IMETHODIMP 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;
+  BrowseOperator* mContext;
+};
+
+class RegisterReplyRunnable final : public nsRunnable
+{
+public:
+  RegisterReplyRunnable(DNSServiceRef aSdRef,
+                        DNSServiceFlags aFlags,
+                        DNSServiceErrorType aErrorCode,
+                        const nsACString& aName,
+                        const nsACString& aRegType,
+                        const nsACString& aDomain,
+                        RegisterOperator* aContext);
+
+  NS_IMETHODIMP 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;
+  RegisterOperator* mContext;
+};
+
+class ResolveReplyRunnable final : public nsRunnable
+{
+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_IMETHODIMP 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;
+  nsRefPtr<ResolveOperator> mContext;
+};
+
+} // namespace net
+} // namespace mozilla
+
+ #endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; c-basic-offset: 4; 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'] == 'gonk' and CONFIG['ANDROID_VERSION'] >= '16':
+    UNIFIED_SOURCES += [
+        'MDNSResponderOperator.cpp',
+        'MDNSResponderReply.cpp',
+        'nsDNSServiceDiscovery.cpp',
+    ]
+
+    CXXFLAGS += [
+        '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
+            'external/mdnsresponder/mDNSShared',
+        ]
+    ]
+
+    LOCAL_INCLUDES += [
+      '/netwerk/base',
+    ]
+
+UNIFIED_SOURCES += [
+    'nsDNSServiceInfo.cpp',
+    'nsMulticastDNSModule.cpp',
+]
+
+FAIL_ON_WARNINGS = True
+
+include('/ipc/chromium/chromium-config.mozbuild')
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
@@ -0,0 +1,230 @@
+/* -*- 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 <cutils/properties.h>
+#include "MDNSResponderOperator.h"
+#include "nsICancelable.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+void
+StartService()
+{
+  char value[PROPERTY_VALUE_MAX] = { '\0' };
+  property_get("init.svc.mdnsd", value, "");
+
+  if (strcmp(value, "running") == 0) {
+    return;
+  }
+  property_set("ctl.start", "mdnsd");
+}
+
+void
+StopService()
+{
+  char value[PROPERTY_VALUE_MAX] = { '\0' };
+  property_get("init.svc.mdnsd", value, "");
+
+  if (strcmp(value, "stopped") == 0) {
+    return;
+  }
+  property_set("ctl.stop", "mdnsd");
+}
+
+class DiscoveryRequest final : public nsICancelable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICANCELABLE
+
+  explicit DiscoveryRequest(nsDNSServiceDiscovery* aService,
+                            nsIDNSServiceDiscoveryListener* aListener);
+
+private:
+  virtual ~DiscoveryRequest() { Cancel(NS_OK); }
+
+  nsRefPtr<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
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICANCELABLE
+
+  explicit RegisterRequest(nsDNSServiceDiscovery* aService,
+                           nsIDNSRegistrationListener* aListener);
+
+private:
+  virtual ~RegisterRequest() { Cancel(NS_OK); }
+
+  nsRefPtr<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)
+
+nsresult
+nsDNSServiceDiscovery::Init()
+{
+  StartService();
+  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;
+  }
+
+  nsRefPtr<BrowseOperator> browserOp = new BrowseOperator(aServiceType,
+                                                          aListener);
+  if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) {
+    return rv;
+  }
+
+  mDiscoveryMap.Put(aListener, browserOp);
+
+  nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener);
+  req.forget(aRetVal);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::StopDiscovery(nsIDNSServiceDiscoveryListener* aListener)
+{
+  nsresult rv;
+
+  nsRefPtr<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;
+  }
+
+  nsRefPtr<RegisterOperator> registerOp = new RegisterOperator(aServiceInfo,
+                                                               aListener);
+  if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) {
+    return rv;
+  }
+
+  mRegisterMap.Put(aListener, registerOp);
+
+  nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener);
+  req.forget(aRetVal);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::UnregisterService(nsIDNSRegistrationListener* aListener)
+{
+  nsresult rv;
+
+  nsRefPtr<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)
+{
+  nsresult rv;
+
+  nsRefPtr<ResolveOperator> resolveOp = new ResolveOperator(aServiceInfo,
+                                                            aListener);
+  if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
@@ -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 "nsRefPtr.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 in this function. However, the function returns
+  ** without waiting. 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() = default;
+
+  nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap;
+  nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.cpp
@@ -0,0 +1,183 @@
+/* -*- 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"
+
+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))) {
+    NS_WARN_IF(NS_FAILED(SetHost(str)));
+  }
+  if (NS_SUCCEEDED(aServiceInfo->GetPort(&value))) {
+    NS_WARN_IF(NS_FAILED(SetPort(value)));
+  }
+  if (NS_SUCCEEDED(aServiceInfo->GetServiceName(str))) {
+    NS_WARN_IF(NS_FAILED(SetServiceName(str)));
+  }
+  if (NS_SUCCEEDED(aServiceInfo->GetServiceType(str))) {
+    NS_WARN_IF(NS_FAILED(SetServiceType(str)));
+  }
+  if (NS_SUCCEEDED(aServiceInfo->GetDomainName(str))) {
+    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;
+      NS_WARN_IF(NS_FAILED(enumerator->GetNext(getter_AddRefs(element))));
+      nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
+      MOZ_ASSERT(property);
+
+      nsAutoString name;
+      nsCOMPtr<nsIVariant> value;
+      NS_WARN_IF(NS_FAILED(property->GetName(name)));
+      NS_WARN_IF(NS_FAILED(property->GetValue(getter_AddRefs(value))));
+      NS_WARN_IF(NS_FAILED(newAttributes->SetPropertyAsInterface(name, value)));
+    }
+
+    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::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 = true;
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h
@@ -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_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;
+  uint16_t mPort = 0;
+  nsCString mServiceName;
+  nsCString mServiceType;
+  nsCString mDomainName;
+  nsCOMPtr<nsIPropertyBag2> mAttributes;
+
+  bool mIsHostSet = 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
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
@@ -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_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;
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; 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'
new file mode 100644
--- /dev/null
+++ b/netwerk/dns/mdns/nsIDNSServiceDiscovery.idl
@@ -0,0 +1,213 @@
+/* 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(112bfa89-1b57-4acf-8287-48e5466c1b39)]
+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 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"
+%}
--- a/netwerk/dns/moz.build
+++ b/netwerk/dns/moz.build
@@ -1,14 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; 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',
 ]
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -253,16 +253,21 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         'mtp',
     ]
 
     if int(CONFIG['ANDROID_VERSION']) >= 17:
         OS_LIBS += [
             'sync',
         ]
 
+    if CONFIG['ANDROID_VERSION'] >= '16':
+        OS_LIBS += [
+            'mdnssd',
+        ]
+
 if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
     OS_LIBS += [
         'stagefright_foundation',
     ]
 
 OS_LIBS += CONFIG['ICONV_LIBS']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':