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 246923 dda118cb0bcc60bcc66ee42f4bae5d5338f46d33
parent 246922 bdc2ca02b0804f61585d5dfb64cfc5d776636a55
child 246924 7ada5429c80deeedcefbd2925355d3f6c80e1bc6
push id15945
push userryanvm@gmail.com
push dateWed, 03 Jun 2015 12:58:01 +0000
treeherderb2g-inbound@ce7b42bbf717 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1115480
milestone41.0a1
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':