summaryrefslogtreecommitdiffstats
path: root/netwerk/wifi
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/wifi/moz.build53
-rw-r--r--netwerk/wifi/nsIWifiAccessPoint.idl41
-rw-r--r--netwerk/wifi/nsIWifiListener.idl29
-rw-r--r--netwerk/wifi/nsIWifiMonitor.idl24
-rw-r--r--netwerk/wifi/nsWifiAccessPoint.cpp81
-rw-r--r--netwerk/wifi/nsWifiAccessPoint.h79
-rw-r--r--netwerk/wifi/nsWifiMonitor.cpp258
-rw-r--r--netwerk/wifi/nsWifiMonitor.h83
-rw-r--r--netwerk/wifi/nsWifiScannerDBus.cpp375
-rw-r--r--netwerk/wifi/nsWifiScannerDBus.h44
-rw-r--r--netwerk/wifi/nsWifiScannerFreeBSD.cpp168
-rw-r--r--netwerk/wifi/nsWifiScannerMac.cpp51
-rw-r--r--netwerk/wifi/nsWifiScannerSolaris.cpp144
-rw-r--r--netwerk/wifi/nsWifiScannerWin.cpp64
-rw-r--r--netwerk/wifi/osx_corewlan.mm100
-rw-r--r--netwerk/wifi/osx_wifi.h119
-rw-r--r--netwerk/wifi/win_wifiScanner.cpp169
-rw-r--r--netwerk/wifi/win_wifiScanner.h32
-rw-r--r--netwerk/wifi/win_wlanLibrary.cpp122
-rw-r--r--netwerk/wifi/win_wlanLibrary.h54
20 files changed, 2090 insertions, 0 deletions
diff --git a/netwerk/wifi/moz.build b/netwerk/wifi/moz.build
new file mode 100644
index 0000000000..13f348c776
--- /dev/null
+++ b/netwerk/wifi/moz.build
@@ -0,0 +1,53 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsIWifiAccessPoint.idl",
+ "nsIWifiListener.idl",
+ "nsIWifiMonitor.idl",
+]
+
+XPIDL_MODULE = "necko_wifi"
+
+UNIFIED_SOURCES += [
+ "nsWifiAccessPoint.cpp",
+ "nsWifiMonitor.cpp",
+]
+
+if CONFIG["OS_ARCH"] == "Darwin":
+ UNIFIED_SOURCES += [
+ "nsWifiScannerMac.cpp",
+ ]
+ SOURCES += [
+ "osx_corewlan.mm",
+ ]
+elif CONFIG["OS_ARCH"] in ("DragonFly", "FreeBSD"):
+ UNIFIED_SOURCES += [
+ "nsWifiScannerFreeBSD.cpp",
+ ]
+elif CONFIG["OS_ARCH"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "nsWifiScannerWin.cpp",
+ "win_wifiScanner.cpp",
+ "win_wlanLibrary.cpp",
+ ]
+elif CONFIG["OS_ARCH"] == "SunOS":
+ CXXFLAGS += CONFIG["GLIB_CFLAGS"]
+ UNIFIED_SOURCES += [
+ "nsWifiScannerSolaris.cpp",
+ ]
+
+if CONFIG["NECKO_WIFI_DBUS"]:
+ UNIFIED_SOURCES += [
+ "nsWifiScannerDBus.cpp",
+ ]
+
+if CONFIG["NECKO_WIFI_DBUS"]:
+ CXXFLAGS += CONFIG["MOZ_DBUS_GLIB_CFLAGS"]
+
+FINAL_LIBRARY = "xul"
+
+REQUIRES_UNIFIED_BUILD = True
diff --git a/netwerk/wifi/nsIWifiAccessPoint.idl b/netwerk/wifi/nsIWifiAccessPoint.idl
new file mode 100644
index 0000000000..c98b42edc4
--- /dev/null
+++ b/netwerk/wifi/nsIWifiAccessPoint.idl
@@ -0,0 +1,41 @@
+/* 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"
+
+[scriptable, uuid(E28E614F-8F86-44FF-BCF5-5F18225834A0)]
+interface nsIWifiAccessPoint : nsISupports
+{
+
+ /*
+ * The mac address of the WiFi node. The format of this string is:
+ * XX-XX-XX-XX-XX-XX
+ */
+
+ readonly attribute ACString mac;
+
+ /*
+ * Public name of a wireless network. The charset of this string is ASCII.
+ * This string will be null if not available.
+ *
+ * Note that this is a conversion of the SSID which makes it "displayable".
+ * for any comparisons, you want to use the Raw SSID.
+ */
+
+ readonly attribute AString ssid;
+
+ /*
+ * Public name of a wireless network. These are the bytes that are read off
+ * of the network, may contain nulls, and generally shouldn't be displayed to
+ * the user.
+ *
+ */
+
+ readonly attribute ACString rawSSID;
+
+ /*
+ * Current signal strength measured in dBm.
+ */
+ readonly attribute long signal;
+};
diff --git a/netwerk/wifi/nsIWifiListener.idl b/netwerk/wifi/nsIWifiListener.idl
new file mode 100644
index 0000000000..49024f405f
--- /dev/null
+++ b/netwerk/wifi/nsIWifiListener.idl
@@ -0,0 +1,29 @@
+/* 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 nsIWifiAccessPoint;
+
+[scriptable, uuid(BCD4BEDE-F4A5-4A62-9071-D7A60174E376)]
+interface nsIWifiListener : nsISupports
+{
+ /*
+ * Called when the list of access points changes.
+ *
+ * @param accessPoints An array of nsIWifiAccessPoint representing all
+ * access points in view.
+ */
+
+ void onChange(in Array<nsIWifiAccessPoint> accessPoints);
+
+ /*
+ * Called when there is a problem with listening to wifi
+ *
+ * @param error the error which caused this event. The
+ * error values will be nsresult codes.
+ */
+
+ void onError(in nsresult error);
+};
diff --git a/netwerk/wifi/nsIWifiMonitor.idl b/netwerk/wifi/nsIWifiMonitor.idl
new file mode 100644
index 0000000000..aaa318d255
--- /dev/null
+++ b/netwerk/wifi/nsIWifiMonitor.idl
@@ -0,0 +1,24 @@
+/* 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 nsIWifiListener;
+
+[scriptable, uuid(F289701E-D9AF-4685-BC2F-E4226FF7C018)]
+interface nsIWifiMonitor : nsISupports
+{
+
+ /*
+ * startWatching
+ * aListener will be called once, then each time the list of wifi access points change.
+ */
+ void startWatching(in nsIWifiListener aListener);
+
+ /*
+ * stopWatching
+ * cancels all notifications to the |aListener|.
+ */
+ void stopWatching(in nsIWifiListener aListener);
+};
diff --git a/netwerk/wifi/nsWifiAccessPoint.cpp b/netwerk/wifi/nsWifiAccessPoint.cpp
new file mode 100644
index 0000000000..db2342772c
--- /dev/null
+++ b/netwerk/wifi/nsWifiAccessPoint.cpp
@@ -0,0 +1,81 @@
+/* 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 "nsWifiAccessPoint.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+
+extern mozilla::LazyLogModule gWifiMonitorLog;
+#define LOG(args) MOZ_LOG(gWifiMonitorLog, mozilla::LogLevel::Debug, args)
+
+NS_IMPL_ISUPPORTS(nsWifiAccessPoint, nsIWifiAccessPoint)
+
+nsWifiAccessPoint::nsWifiAccessPoint() {
+ // make sure these are null terminated (because we are paranoid)
+ mMac[0] = '\0';
+ mSsid[0] = '\0';
+ mSsidLen = 0;
+ mSignal = -1000;
+}
+
+NS_IMETHODIMP nsWifiAccessPoint::GetMac(nsACString& aMac) {
+ aMac.Assign(mMac);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWifiAccessPoint::GetSsid(nsAString& aSsid) {
+ // just assign and embedded nulls will truncate resulting
+ // in a displayable string.
+ aSsid.AssignASCII(mSsid);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWifiAccessPoint::GetRawSSID(nsACString& aRawSsid) {
+ aRawSsid.Assign(mSsid, mSsidLen); // SSIDs are 32 chars long
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWifiAccessPoint::GetSignal(int32_t* aSignal) {
+ NS_ENSURE_ARG(aSignal);
+ *aSignal = mSignal;
+ return NS_OK;
+}
+
+// Helper functions:
+
+bool AccessPointsEqual(nsCOMArray<nsWifiAccessPoint>& a,
+ nsCOMArray<nsWifiAccessPoint>& b) {
+ if (a.Count() != b.Count()) {
+ LOG(("AccessPoint lists have different lengths\n"));
+ return false;
+ }
+
+ for (int32_t i = 0; i < a.Count(); i++) {
+ LOG(("++ Looking for %s\n", a[i]->mSsid));
+ bool found = false;
+ for (int32_t j = 0; j < b.Count(); j++) {
+ LOG((" %s->%s | %s->%s\n", a[i]->mSsid, b[j]->mSsid, a[i]->mMac,
+ b[j]->mMac));
+ if (!strcmp(a[i]->mSsid, b[j]->mSsid) &&
+ !strcmp(a[i]->mMac, b[j]->mMac) && a[i]->mSignal == b[j]->mSignal) {
+ found = true;
+ }
+ }
+ if (!found) return false;
+ }
+ LOG((" match!\n"));
+ return true;
+}
+
+void ReplaceArray(nsCOMArray<nsWifiAccessPoint>& a,
+ nsCOMArray<nsWifiAccessPoint>& b) {
+ a.Clear();
+
+ // better way to copy?
+ for (int32_t i = 0; i < b.Count(); i++) {
+ a.AppendObject(b[i]);
+ }
+
+ b.Clear();
+}
diff --git a/netwerk/wifi/nsWifiAccessPoint.h b/netwerk/wifi/nsWifiAccessPoint.h
new file mode 100644
index 0000000000..b6778a7fef
--- /dev/null
+++ b/netwerk/wifi/nsWifiAccessPoint.h
@@ -0,0 +1,79 @@
+/* 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 __nsWifiAccessPoint__
+#define __nsWifiAccessPoint__
+
+#include <algorithm>
+#include "nsWifiMonitor.h"
+#include "nsIWifiAccessPoint.h"
+
+#include "nsString.h"
+#include "nsCOMArray.h"
+#include "mozilla/ArrayUtils.h" // ArrayLength
+#include "mozilla/Attributes.h"
+#include "mozilla/Sprintf.h"
+
+class nsWifiAccessPoint final : public nsIWifiAccessPoint {
+ ~nsWifiAccessPoint() = default;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIWIFIACCESSPOINT
+
+ nsWifiAccessPoint();
+
+ char mMac[18]{0};
+ int mSignal;
+ char mSsid[33]{0};
+ int mSsidLen;
+
+ void setSignal(int signal) { mSignal = signal; }
+
+ void setMacRaw(const char* aString) {
+ memcpy(mMac, aString, mozilla::ArrayLength(mMac));
+ }
+
+ void setMac(const unsigned char mac_as_int[6]) {
+ // mac_as_int is big-endian. Write in byte chunks.
+ // Format is XX-XX-XX-XX-XX-XX.
+
+ const unsigned char holder[6] = {0};
+ if (!mac_as_int) {
+ mac_as_int = holder;
+ }
+
+ static const char* kMacFormatString = ("%02x-%02x-%02x-%02x-%02x-%02x");
+
+ SprintfLiteral(mMac, kMacFormatString, mac_as_int[0], mac_as_int[1],
+ mac_as_int[2], mac_as_int[3], mac_as_int[4], mac_as_int[5]);
+
+ mMac[17] = 0;
+ }
+
+ void setSSIDRaw(const char* aSSID, size_t len) {
+ mSsidLen = std::min(len, mozilla::ArrayLength(mSsid));
+ memcpy(mSsid, aSSID, mSsidLen);
+ }
+
+ void setSSID(const char* aSSID, unsigned long len) {
+ if (aSSID && (len < sizeof(mSsid))) {
+ strncpy(mSsid, aSSID, len);
+ mSsid[len] = 0;
+ mSsidLen = len;
+ } else {
+ mSsid[0] = 0;
+ mSsidLen = 0;
+ }
+ }
+};
+
+// Helper functions
+
+bool AccessPointsEqual(nsCOMArray<nsWifiAccessPoint>& a,
+ nsCOMArray<nsWifiAccessPoint>& b);
+void ReplaceArray(nsCOMArray<nsWifiAccessPoint>& a,
+ nsCOMArray<nsWifiAccessPoint>& b);
+
+#endif
diff --git a/netwerk/wifi/nsWifiMonitor.cpp b/netwerk/wifi/nsWifiMonitor.cpp
new file mode 100644
index 0000000000..a3f846b74c
--- /dev/null
+++ b/netwerk/wifi/nsWifiMonitor.cpp
@@ -0,0 +1,258 @@
+/* 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 "nsCOMPtr.h"
+#include "nsProxyRelease.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsWifiMonitor.h"
+#include "nsWifiAccessPoint.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Services.h"
+
+#ifdef XP_MACOSX
+# include "nsCocoaFeatures.h"
+#endif
+
+using namespace mozilla;
+
+LazyLogModule gWifiMonitorLog("WifiMonitor");
+
+NS_IMPL_ISUPPORTS(nsWifiMonitor, nsIRunnable, nsIObserver, nsIWifiMonitor)
+
+nsWifiMonitor::nsWifiMonitor()
+ : mKeepGoing(true),
+ mThreadComplete(false),
+ mReentrantMonitor("nsWifiMonitor.mReentrantMonitor") {
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) obsSvc->AddObserver(this, "xpcom-shutdown", false);
+
+ LOG(("@@@@@ wifimonitor created\n"));
+}
+
+NS_IMETHODIMP
+nsWifiMonitor::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!strcmp(topic, "xpcom-shutdown")) {
+ LOG(("Shutting down\n"));
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mKeepGoing = false;
+ mon.Notify();
+ mThread = nullptr;
+ }
+ return NS_OK;
+}
+
+uint32_t nsWifiMonitor::GetMonitorThreadStackSize() {
+#ifdef XP_MACOSX
+ // If this ASSERT fails, we've increased our default stack size and
+ // may no longer need to special-case the stack size on macOS.
+ MOZ_ASSERT(kMacOS13MonitorStackSize > nsIThreadManager::DEFAULT_STACK_SIZE);
+ return nsCocoaFeatures::OnVenturaOrLater()
+ ? kMacOS13MonitorStackSize
+ : nsIThreadManager::DEFAULT_STACK_SIZE;
+#else
+ return nsIThreadManager::DEFAULT_STACK_SIZE;
+#endif
+}
+
+NS_IMETHODIMP nsWifiMonitor::StartWatching(nsIWifiListener* aListener) {
+ LOG(("nsWifiMonitor::StartWatching %p thread %p listener %p\n", this,
+ mThread.get(), aListener));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aListener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (!mKeepGoing) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = NS_OK;
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mThreadComplete) {
+ // generally there is just one thread for the lifetime of the service,
+ // but if DoScan returns with an error before shutdown (i.e. !mKeepGoing)
+ // then we will respawn the thread.
+ LOG(("nsWifiMonitor::StartWatching %p restarting thread\n", this));
+ mThreadComplete = false;
+ mThread = nullptr;
+ }
+
+ if (!mThread) {
+ rv = NS_NewNamedThread("Wifi Monitor", getter_AddRefs(mThread), this,
+ {.stackSize = GetMonitorThreadStackSize()});
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ mListeners.AppendElement(
+ nsWifiListener(new nsMainThreadPtrHolder<nsIWifiListener>(
+ "nsIWifiListener", aListener)));
+
+ // tell ourselves that we have a new watcher.
+ mon.Notify();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWifiMonitor::StopWatching(nsIWifiListener* aListener) {
+ LOG(("nsWifiMonitor::StopWatching %p thread %p listener %p\n", this,
+ mThread.get(), aListener));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aListener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ for (uint32_t i = 0; i < mListeners.Length(); i++) {
+ if (mListeners[i].mListener == aListener) {
+ mListeners.RemoveElementAt(i);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+using WifiListenerArray = nsTArray<nsMainThreadPtrHandle<nsIWifiListener>>;
+
+class nsPassErrorToWifiListeners final : public nsIRunnable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ nsPassErrorToWifiListeners(UniquePtr<WifiListenerArray>&& aListeners,
+ nsresult aResult)
+ : mListeners(std::move(aListeners)), mResult(aResult) {}
+
+ private:
+ ~nsPassErrorToWifiListeners() = default;
+ UniquePtr<WifiListenerArray> mListeners;
+ nsresult mResult;
+};
+
+NS_IMPL_ISUPPORTS(nsPassErrorToWifiListeners, nsIRunnable)
+
+NS_IMETHODIMP nsPassErrorToWifiListeners::Run() {
+ LOG(("About to send error to the wifi listeners\n"));
+ for (size_t i = 0; i < mListeners->Length(); i++) {
+ (*mListeners)[i]->OnError(mResult);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWifiMonitor::Run() {
+ LOG(("@@@@@ wifi monitor run called\n"));
+
+ nsresult rv = DoScan();
+ LOG(("@@@@@ wifi monitor run::doscan complete %" PRIx32 "\n",
+ static_cast<uint32_t>(rv)));
+
+ UniquePtr<WifiListenerArray> currentListeners;
+ bool doError = false;
+
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mKeepGoing && NS_FAILED(rv)) {
+ doError = true;
+ currentListeners = MakeUnique<WifiListenerArray>(mListeners.Length());
+ for (uint32_t i = 0; i < mListeners.Length(); i++) {
+ currentListeners->AppendElement(mListeners[i].mListener);
+ }
+ }
+ mThreadComplete = true;
+ }
+
+ if (doError) {
+ nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
+ if (!target) return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIRunnable> runnable(
+ new nsPassErrorToWifiListeners(std::move(currentListeners), rv));
+ if (!runnable) return NS_ERROR_OUT_OF_MEMORY;
+
+ target->Dispatch(runnable, NS_DISPATCH_SYNC);
+ }
+
+ LOG(("@@@@@ wifi monitor run complete\n"));
+ return NS_OK;
+}
+
+class nsCallWifiListeners final : public nsIRunnable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ nsCallWifiListeners(WifiListenerArray&& aListeners,
+ nsTArray<RefPtr<nsIWifiAccessPoint>>&& aAccessPoints)
+ : mListeners(std::move(aListeners)),
+ mAccessPoints(std::move(aAccessPoints)) {}
+
+ private:
+ ~nsCallWifiListeners() = default;
+ const WifiListenerArray mListeners;
+ const nsTArray<RefPtr<nsIWifiAccessPoint>> mAccessPoints;
+};
+
+NS_IMPL_ISUPPORTS(nsCallWifiListeners, nsIRunnable)
+
+NS_IMETHODIMP nsCallWifiListeners::Run() {
+ LOG(("About to send data to the wifi listeners\n"));
+ for (const auto& listener : mListeners) {
+ listener->OnChange(mAccessPoints);
+ }
+ return NS_OK;
+}
+
+nsresult nsWifiMonitor::CallWifiListeners(
+ const nsCOMArray<nsWifiAccessPoint>& aAccessPoints,
+ bool aAccessPointsChanged) {
+ WifiListenerArray currentListeners;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ currentListeners.SetCapacity(mListeners.Length());
+
+ for (auto& listener : mListeners) {
+ if (!listener.mHasSentData || aAccessPointsChanged) {
+ listener.mHasSentData = true;
+ currentListeners.AppendElement(listener.mListener);
+ }
+ }
+ }
+
+ if (!currentListeners.IsEmpty()) {
+ uint32_t resultCount = aAccessPoints.Count();
+ nsTArray<RefPtr<nsIWifiAccessPoint>> accessPoints(resultCount);
+
+ for (uint32_t i = 0; i < resultCount; i++) {
+ accessPoints.AppendElement(aAccessPoints[i]);
+ }
+
+ nsCOMPtr<nsIThread> thread = do_GetMainThread();
+ if (!thread) return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIRunnable> runnable(new nsCallWifiListeners(
+ std::move(currentListeners), std::move(accessPoints)));
+ if (!runnable) return NS_ERROR_OUT_OF_MEMORY;
+
+ thread->Dispatch(runnable, NS_DISPATCH_SYNC);
+ }
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/nsWifiMonitor.h b/netwerk/wifi/nsWifiMonitor.h
new file mode 100644
index 0000000000..aa71760467
--- /dev/null
+++ b/netwerk/wifi/nsWifiMonitor.h
@@ -0,0 +1,83 @@
+/* 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 __nsWifiMonitor__
+#define __nsWifiMonitor__
+
+#include "nsIWifiMonitor.h"
+#include "nsCOMPtr.h"
+#include "nsProxyRelease.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsCOMArray.h"
+#include "nsIWifiListener.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Logging.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+
+#ifdef XP_WIN
+# include "win_wifiScanner.h"
+#endif
+
+extern mozilla::LazyLogModule gWifiMonitorLog;
+#define LOG(args) MOZ_LOG(gWifiMonitorLog, mozilla::LogLevel::Debug, args)
+
+class nsWifiAccessPoint;
+
+#define kDefaultWifiScanInterval 5 /* seconds */
+
+#ifdef XP_MACOSX
+// Use a larger stack size for the monitor thread on macOS 13+
+// to accommodate Core WLAN making large stack allocations.
+# define kMacOS13MonitorStackSize (512 * 1024)
+#endif
+
+class nsWifiListener {
+ public:
+ explicit nsWifiListener(nsMainThreadPtrHolder<nsIWifiListener>* aListener) {
+ mListener = aListener;
+ mHasSentData = false;
+ }
+ ~nsWifiListener() = default;
+
+ nsMainThreadPtrHandle<nsIWifiListener> mListener;
+ bool mHasSentData;
+};
+
+class nsWifiMonitor final : nsIRunnable, nsIWifiMonitor, nsIObserver {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIWIFIMONITOR
+ NS_DECL_NSIRUNNABLE
+ NS_DECL_NSIOBSERVER
+
+ nsWifiMonitor();
+
+ private:
+ ~nsWifiMonitor() = default;
+
+ nsresult DoScan();
+
+ nsresult CallWifiListeners(const nsCOMArray<nsWifiAccessPoint>& aAccessPoints,
+ bool aAccessPointsChanged);
+
+ uint32_t GetMonitorThreadStackSize();
+
+ mozilla::Atomic<bool> mKeepGoing;
+ mozilla::Atomic<bool> mThreadComplete;
+ nsCOMPtr<nsIThread> mThread; // only accessed on MainThread
+
+ nsTArray<nsWifiListener> mListeners MOZ_GUARDED_BY(mReentrantMonitor);
+
+ mozilla::ReentrantMonitor mReentrantMonitor;
+
+#ifdef XP_WIN
+ mozilla::UniquePtr<WinWifiScanner> mWinWifiScanner;
+#endif
+};
+
+#endif
diff --git a/netwerk/wifi/nsWifiScannerDBus.cpp b/netwerk/wifi/nsWifiScannerDBus.cpp
new file mode 100644
index 0000000000..d74341078a
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerDBus.cpp
@@ -0,0 +1,375 @@
+/* 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 "nsWifiScannerDBus.h"
+#include "mozilla/DBusHelpers.h"
+#include "nsWifiAccessPoint.h"
+
+namespace mozilla {
+
+nsWifiScannerDBus::nsWifiScannerDBus(
+ nsCOMArray<nsWifiAccessPoint>* aAccessPoints)
+ : mAccessPoints(aAccessPoints) {
+ MOZ_ASSERT(mAccessPoints);
+
+ mConnection =
+ already_AddRefed<DBusConnection>(dbus_bus_get(DBUS_BUS_SYSTEM, nullptr));
+
+ if (mConnection) {
+ dbus_connection_setup_with_g_main(mConnection, nullptr);
+ dbus_connection_set_exit_on_disconnect(mConnection, false);
+ }
+
+ MOZ_COUNT_CTOR(nsWifiScannerDBus);
+}
+
+nsWifiScannerDBus::~nsWifiScannerDBus() { MOZ_COUNT_DTOR(nsWifiScannerDBus); }
+
+nsresult nsWifiScannerDBus::Scan() {
+ if (!mConnection) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return SendGetDevices();
+}
+
+// http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
+// Refer to function dbus_connection_send_with_reply_and_block.
+static const uint32_t DBUS_DEFAULT_TIMEOUT = -1;
+
+nsresult nsWifiScannerDBus::SendGetDevices() {
+ RefPtr<DBusMessage> msg =
+ already_AddRefed<DBusMessage>(dbus_message_new_method_call(
+ "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager",
+ "org.freedesktop.NetworkManager", "GetDevices"));
+ if (!msg) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ RefPtr<DBusMessage> reply =
+ already_AddRefed<DBusMessage>(dbus_connection_send_with_reply_and_block(
+ mConnection, msg, DBUS_DEFAULT_TIMEOUT, &err));
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return NS_ERROR_FAILURE;
+ }
+
+ return IdentifyDevices(reply);
+}
+
+nsresult nsWifiScannerDBus::SendGetDeviceType(const char* aPath) {
+ RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
+ dbus_message_new_method_call("org.freedesktop.NetworkManager", aPath,
+ "org.freedesktop.DBus.Properties", "Get"));
+ if (!msg) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusMessageIter argsIter;
+ dbus_message_iter_init_append(msg, &argsIter);
+
+ const char* paramInterface = "org.freedesktop.NetworkManager.Device";
+ if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING,
+ &paramInterface)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* paramDeviceType = "DeviceType";
+ if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING,
+ &paramDeviceType)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ RefPtr<DBusMessage> reply =
+ already_AddRefed<DBusMessage>(dbus_connection_send_with_reply_and_block(
+ mConnection, msg, DBUS_DEFAULT_TIMEOUT, &err));
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return NS_ERROR_FAILURE;
+ }
+
+ return IdentifyDeviceType(reply, aPath);
+}
+
+nsresult nsWifiScannerDBus::SendGetAccessPoints(const char* aPath) {
+ RefPtr<DBusMessage> msg =
+ already_AddRefed<DBusMessage>(dbus_message_new_method_call(
+ "org.freedesktop.NetworkManager", aPath,
+ "org.freedesktop.NetworkManager.Device.Wireless", "GetAccessPoints"));
+ if (!msg) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ RefPtr<DBusMessage> reply =
+ already_AddRefed<DBusMessage>(dbus_connection_send_with_reply_and_block(
+ mConnection, msg, DBUS_DEFAULT_TIMEOUT, &err));
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ // In the GetAccessPoints case, if there are no access points, error is set.
+ // We don't want to error out here.
+ return NS_OK;
+ }
+
+ return IdentifyAccessPoints(reply);
+}
+
+nsresult nsWifiScannerDBus::SendGetAPProperties(const char* aPath) {
+ RefPtr<DBusMessage> msg =
+ already_AddRefed<DBusMessage>(dbus_message_new_method_call(
+ "org.freedesktop.NetworkManager", aPath,
+ "org.freedesktop.DBus.Properties", "GetAll"));
+ if (!msg) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusMessageIter argsIter;
+ dbus_message_iter_init_append(msg, &argsIter);
+
+ const char* param = "org.freedesktop.NetworkManager.AccessPoint";
+ if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, &param)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ RefPtr<DBusMessage> reply =
+ already_AddRefed<DBusMessage>(dbus_connection_send_with_reply_and_block(
+ mConnection, msg, DBUS_DEFAULT_TIMEOUT, &err));
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return NS_ERROR_FAILURE;
+ }
+
+ return IdentifyAPProperties(reply);
+}
+
+nsresult nsWifiScannerDBus::IdentifyDevices(DBusMessage* aMsg) {
+ DBusMessageIter iter;
+ nsresult rv = GetDBusIterator(aMsg, &iter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char* devicePath;
+ do {
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+ return NS_ERROR_FAILURE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &devicePath);
+ if (!devicePath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = SendGetDeviceType(devicePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } while (dbus_message_iter_next(&iter));
+
+ return NS_OK;
+}
+
+nsresult nsWifiScannerDBus::IdentifyDeviceType(DBusMessage* aMsg,
+ const char* aDevicePath) {
+ DBusMessageIter args;
+ if (!dbus_message_iter_init(aMsg, &args)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusMessageIter variantIter;
+ dbus_message_iter_recurse(&args, &variantIter);
+ if (dbus_message_iter_get_arg_type(&variantIter) != DBUS_TYPE_UINT32) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t deviceType;
+ dbus_message_iter_get_basic(&variantIter, &deviceType);
+
+ // http://projects.gnome.org/NetworkManager/developers/api/07/spec-07.html
+ // Refer to NM_DEVICE_TYPE_WIFI under NM_DEVICE_TYPE.
+ const uint32_t NM_DEVICE_TYPE_WIFI = 2;
+ nsresult rv = NS_OK;
+ if (deviceType == NM_DEVICE_TYPE_WIFI) {
+ rv = SendGetAccessPoints(aDevicePath);
+ }
+
+ return rv;
+}
+
+nsresult nsWifiScannerDBus::IdentifyAccessPoints(DBusMessage* aMsg) {
+ DBusMessageIter iter;
+ nsresult rv = GetDBusIterator(aMsg, &iter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char* path;
+ do {
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+ return NS_ERROR_FAILURE;
+ }
+ dbus_message_iter_get_basic(&iter, &path);
+ if (!path) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = SendGetAPProperties(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } while (dbus_message_iter_next(&iter));
+
+ return NS_OK;
+}
+
+nsresult nsWifiScannerDBus::IdentifyAPProperties(DBusMessage* aMsg) {
+ DBusMessageIter arr;
+ nsresult rv = GetDBusIterator(aMsg, &arr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsWifiAccessPoint> ap = new nsWifiAccessPoint();
+ do {
+ DBusMessageIter dict;
+ dbus_message_iter_recurse(&arr, &dict);
+
+ do {
+ const char* key;
+ dbus_message_iter_get_basic(&dict, &key);
+ if (!key) {
+ return NS_ERROR_FAILURE;
+ }
+ dbus_message_iter_next(&dict);
+
+ DBusMessageIter variant;
+ dbus_message_iter_recurse(&dict, &variant);
+
+ if (!strncmp(key, "Ssid", strlen("Ssid"))) {
+ nsresult rv = StoreSsid(&variant, ap);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+
+ if (!strncmp(key, "HwAddress", strlen("HwAddress"))) {
+ nsresult rv = SetMac(&variant, ap);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+
+ if (!strncmp(key, "Strength", strlen("Strength"))) {
+ if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_BYTE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint8_t strength; // in %
+ dbus_message_iter_get_basic(&variant, &strength);
+ int signal_strength = (strength / 2) - 100; // strength to dB
+ ap->setSignal(signal_strength);
+ }
+ } while (dbus_message_iter_next(&dict));
+ } while (dbus_message_iter_next(&arr));
+
+ mAccessPoints->AppendObject(ap);
+ return NS_OK;
+}
+
+nsresult nsWifiScannerDBus::StoreSsid(DBusMessageIter* aVariant,
+ nsWifiAccessPoint* aAp) {
+ if (dbus_message_iter_get_arg_type(aVariant) != DBUS_TYPE_ARRAY) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DBusMessageIter variantMember;
+ dbus_message_iter_recurse(aVariant, &variantMember);
+
+ const uint32_t MAX_SSID_LEN = 32;
+ char ssid[MAX_SSID_LEN];
+ memset(ssid, '\0', ArrayLength(ssid));
+ uint32_t i = 0;
+ do {
+ if (dbus_message_iter_get_arg_type(&variantMember) != DBUS_TYPE_BYTE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ dbus_message_iter_get_basic(&variantMember, &ssid[i]);
+ i++;
+ } while (dbus_message_iter_next(&variantMember) && i < MAX_SSID_LEN);
+
+ aAp->setSSID(ssid, i);
+ return NS_OK;
+}
+
+nsresult nsWifiScannerDBus::SetMac(DBusMessageIter* aVariant,
+ nsWifiAccessPoint* aAp) {
+ if (dbus_message_iter_get_arg_type(aVariant) != DBUS_TYPE_STRING) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // hwAddress format is XX:XX:XX:XX:XX:XX. Need to convert to XXXXXX format.
+ char* hwAddress;
+ dbus_message_iter_get_basic(aVariant, &hwAddress);
+ if (!hwAddress) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint32_t MAC_LEN = 6;
+ uint8_t macAddress[MAC_LEN];
+ char* savedPtr = nullptr;
+ char* token = strtok_r(hwAddress, ":", &savedPtr);
+ for (unsigned char& macAddres : macAddress) {
+ if (!token) {
+ return NS_ERROR_FAILURE;
+ }
+ macAddres = strtoul(token, nullptr, 16);
+ token = strtok_r(nullptr, ":", &savedPtr);
+ }
+ aAp->setMac(macAddress);
+ return NS_OK;
+}
+
+nsresult nsWifiScannerDBus::GetDBusIterator(DBusMessage* aMsg,
+ DBusMessageIter* aIterArray) {
+ DBusMessageIter iter;
+ if (!dbus_message_iter_init(aMsg, &iter)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ return NS_ERROR_FAILURE;
+ }
+
+ dbus_message_iter_recurse(&iter, aIterArray);
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+nsresult nsWifiMonitor::DoScan() {
+ nsCOMArray<nsWifiAccessPoint> accessPoints;
+ mozilla::nsWifiScannerDBus wifiScanner(&accessPoints);
+ nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
+
+ while (mKeepGoing) {
+ accessPoints.Clear();
+ nsresult rv = wifiScanner.Scan();
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool accessPointsChanged =
+ !AccessPointsEqual(accessPoints, lastAccessPoints);
+ ReplaceArray(lastAccessPoints, accessPoints);
+
+ rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LOG(("waiting on monitor\n"));
+ mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
+ }
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/nsWifiScannerDBus.h b/netwerk/wifi/nsWifiScannerDBus.h
new file mode 100644
index 0000000000..91e802db47
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerDBus.h
@@ -0,0 +1,44 @@
+/* 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 NSWIFIAPSCANNERDBUS_H_
+#define NSWIFIAPSCANNERDBUS_H_
+
+#include "nsCOMArray.h"
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+class nsWifiAccessPoint;
+
+namespace mozilla {
+
+class nsWifiScannerDBus final {
+ public:
+ explicit nsWifiScannerDBus(nsCOMArray<nsWifiAccessPoint>* aAccessPoints);
+ ~nsWifiScannerDBus();
+
+ nsresult Scan();
+
+ private:
+ nsresult SendGetDevices();
+ nsresult SendGetDeviceType(const char* aPath);
+ nsresult SendGetAccessPoints(const char* aPath);
+ nsresult SendGetAPProperties(const char* aPath);
+ nsresult IdentifyDevices(DBusMessage* aMsg);
+ nsresult IdentifyDeviceType(DBusMessage* aMsg, const char* aDevicePath);
+ nsresult IdentifyAccessPoints(DBusMessage* aMsg);
+ nsresult IdentifyAPProperties(DBusMessage* aMsg);
+ nsresult StoreSsid(DBusMessageIter* aVariant, nsWifiAccessPoint* aAp);
+ nsresult SetMac(DBusMessageIter* aVariant, nsWifiAccessPoint* aAp);
+ nsresult GetDBusIterator(DBusMessage* aMsg, DBusMessageIter* aIterArray);
+
+ RefPtr<DBusConnection> mConnection;
+ nsCOMArray<nsWifiAccessPoint>* mAccessPoints;
+};
+
+} // namespace mozilla
+
+#endif // NSWIFIAPSCANNERDBUS_H_
diff --git a/netwerk/wifi/nsWifiScannerFreeBSD.cpp b/netwerk/wifi/nsWifiScannerFreeBSD.cpp
new file mode 100644
index 0000000000..83ec847bdf
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerFreeBSD.cpp
@@ -0,0 +1,168 @@
+/* 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/. */
+
+// Developed by J.R. Oldroyd <fbsd@opal.com>, December 2012.
+
+// For FreeBSD we use the getifaddrs(3) to obtain the list of interfaces
+// and then check for those with an 802.11 media type and able to return
+// a list of stations. This is similar to ifconfig(8).
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#ifdef __DragonFly__
+# include <netproto/802_11/ieee80211_ioctl.h>
+#else
+# include <net80211/ieee80211_ioctl.h>
+#endif
+
+#include <ifaddrs.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "nsWifiAccessPoint.h"
+
+using namespace mozilla;
+
+static nsresult FreeBSDGetAccessPointData(
+ nsCOMArray<nsWifiAccessPoint>& accessPoints) {
+ // get list of interfaces
+ struct ifaddrs* ifal;
+ if (getifaddrs(&ifal) < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ accessPoints.Clear();
+
+ // loop through the interfaces
+ nsresult rv = NS_ERROR_FAILURE;
+ struct ifaddrs* ifa;
+ for (ifa = ifal; ifa; ifa = ifa->ifa_next) {
+ // limit to one interface per address
+ if (ifa->ifa_addr->sa_family != AF_LINK) {
+ continue;
+ }
+
+ // store interface name in socket structure
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_LOCAL;
+
+ // open socket to interface
+ int s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0);
+ if (s < 0) {
+ continue;
+ }
+
+ // clear interface media structure
+ struct ifmediareq ifmr;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifa->ifa_name, sizeof(ifmr.ifm_name));
+
+ // get interface media information
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ close(s);
+ continue;
+ }
+
+ // check interface is a WiFi interface
+ if (IFM_TYPE(ifmr.ifm_active) != IFM_IEEE80211) {
+ close(s);
+ continue;
+ }
+
+ // perform WiFi scan
+ struct ieee80211req i802r;
+ char iscanbuf[32 * 1024];
+ memset(&i802r, 0, sizeof(i802r));
+ strncpy(i802r.i_name, ifa->ifa_name, sizeof(i802r.i_name));
+ i802r.i_type = IEEE80211_IOC_SCAN_RESULTS;
+ i802r.i_data = iscanbuf;
+ i802r.i_len = sizeof(iscanbuf);
+ if (ioctl(s, SIOCG80211, &i802r) < 0) {
+ close(s);
+ continue;
+ }
+
+ // close socket
+ close(s);
+
+ // loop through WiFi networks and build geoloc-lookup structure
+ char* vsr = (char*)i802r.i_data;
+ unsigned len = i802r.i_len;
+ while (len >= sizeof(struct ieee80211req_scan_result)) {
+ struct ieee80211req_scan_result* isr =
+ (struct ieee80211req_scan_result*)vsr;
+
+ // determine size of this entry
+ char* id;
+ int idlen;
+ if (isr->isr_meshid_len) {
+ id = vsr + isr->isr_ie_off + isr->isr_ssid_len;
+ idlen = isr->isr_meshid_len;
+ } else {
+ id = vsr + isr->isr_ie_off;
+ idlen = isr->isr_ssid_len;
+ }
+
+ // copy network data
+ char ssid[IEEE80211_NWID_LEN + 1];
+ strncpy(ssid, id, idlen);
+ ssid[idlen] = '\0';
+ nsWifiAccessPoint* ap = new nsWifiAccessPoint();
+ ap->setSSID(ssid, strlen(ssid));
+ ap->setMac(isr->isr_bssid);
+ ap->setSignal(isr->isr_rssi);
+ accessPoints.AppendObject(ap);
+ rv = NS_OK;
+
+ // log the data
+ LOG(
+ ("FreeBSD access point: "
+ "SSID: %s, MAC: %02x-%02x-%02x-%02x-%02x-%02x, "
+ "Strength: %d, Channel: %dMHz\n",
+ ssid, isr->isr_bssid[0], isr->isr_bssid[1], isr->isr_bssid[2],
+ isr->isr_bssid[3], isr->isr_bssid[4], isr->isr_bssid[5],
+ isr->isr_rssi, isr->isr_freq));
+
+ // increment pointers
+ len -= isr->isr_len;
+ vsr += isr->isr_len;
+ }
+ }
+
+ freeifaddrs(ifal);
+
+ return rv;
+}
+
+nsresult nsWifiMonitor::DoScan() {
+ // Regularly get the access point data.
+
+ nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
+ nsCOMArray<nsWifiAccessPoint> accessPoints;
+
+ do {
+ nsresult rv = FreeBSDGetAccessPointData(accessPoints);
+ if (NS_FAILED(rv)) return rv;
+
+ bool accessPointsChanged =
+ !AccessPointsEqual(accessPoints, lastAccessPoints);
+ ReplaceArray(lastAccessPoints, accessPoints);
+
+ rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // wait for some reasonable amount of time. pref?
+ LOG(("waiting on monitor\n"));
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
+ } while (mKeepGoing);
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/nsWifiScannerMac.cpp b/netwerk/wifi/nsWifiScannerMac.cpp
new file mode 100644
index 0000000000..4b8757fd10
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerMac.cpp
@@ -0,0 +1,51 @@
+/* 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 <mach-o/dyld.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "osx_wifi.h"
+
+#include "nsCOMArray.h"
+#include "nsWifiMonitor.h"
+#include "nsWifiAccessPoint.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+
+// defined in osx_corewlan.mm
+// basically replaces accesspoints in the passed reference
+// it lives in a separate file so that we can use objective c.
+extern nsresult GetAccessPointsFromWLAN(
+ nsCOMArray<nsWifiAccessPoint>& accessPoints);
+
+nsresult nsWifiMonitor::DoScan() {
+ // Regularly get the access point data.
+
+ nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
+ nsCOMArray<nsWifiAccessPoint> accessPoints;
+
+ do {
+ nsresult rv = GetAccessPointsFromWLAN(accessPoints);
+ if (NS_FAILED(rv)) return rv;
+
+ bool accessPointsChanged =
+ !AccessPointsEqual(accessPoints, lastAccessPoints);
+ ReplaceArray(lastAccessPoints, accessPoints);
+
+ rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // wait for some reasonable amount of time. pref?
+ LOG(("waiting on monitor\n"));
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
+ } while (mKeepGoing);
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/nsWifiScannerSolaris.cpp b/netwerk/wifi/nsWifiScannerSolaris.cpp
new file mode 100644
index 0000000000..fdcdf37a09
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerSolaris.cpp
@@ -0,0 +1,144 @@
+/* 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 "nsWifiMonitor.h"
+#include "nsWifiAccessPoint.h"
+
+#include "nsCRT.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+
+#include <glib.h>
+
+#define DLADM_STRSIZE 256
+#define DLADM_SECTIONS 3
+
+using namespace mozilla;
+
+struct val_strength_t {
+ const char* strength_name;
+ int signal_value;
+};
+
+static val_strength_t strength_vals[] = {{"very weak", -112},
+ {"weak", -88},
+ {"good", -68},
+ {"very good", -40},
+ {"excellent", -16}};
+
+static nsWifiAccessPoint* do_parse_str(char* bssid_str, char* essid_str,
+ char* strength) {
+ unsigned char mac_as_int[6] = {0};
+ sscanf(bssid_str, "%x:%x:%x:%x:%x:%x", &mac_as_int[0], &mac_as_int[1],
+ &mac_as_int[2], &mac_as_int[3], &mac_as_int[4], &mac_as_int[5]);
+
+ int signal = 0;
+ uint32_t strength_vals_count = sizeof(strength_vals) / sizeof(val_strength_t);
+ for (uint32_t i = 0; i < strength_vals_count; i++) {
+ if (!nsCRT::strncasecmp(strength, strength_vals[i].strength_name,
+ DLADM_STRSIZE)) {
+ signal = strength_vals[i].signal_value;
+ break;
+ }
+ }
+
+ nsWifiAccessPoint* ap = new nsWifiAccessPoint();
+ if (ap) {
+ ap->setMac(mac_as_int);
+ ap->setSignal(signal);
+ size_t len = essid_str ? strnlen(essid_str, DLADM_STRSIZE) : 0;
+ ap->setSSID(essid_str, len);
+ }
+ return ap;
+}
+
+static void do_dladm(nsCOMArray<nsWifiAccessPoint>& accessPoints) {
+ GError* err = nullptr;
+ char* sout = nullptr;
+ char* serr = nullptr;
+ int exit_status = 0;
+ char* dladm_args[] = {
+ "/usr/bin/pfexec", "/usr/sbin/dladm", "scan-wifi", "-p", "-o",
+ "BSSID,ESSID,STRENGTH"};
+
+ gboolean rv = g_spawn_sync("/", dladm_args, nullptr, (GSpawnFlags)0, nullptr,
+ nullptr, &sout, &serr, &exit_status, &err);
+ if (rv && !exit_status) {
+ char wlan[DLADM_SECTIONS][DLADM_STRSIZE + 1];
+ uint32_t section = 0;
+ uint32_t sout_scan = 0;
+ uint32_t wlan_put = 0;
+ bool escape = false;
+ nsWifiAccessPoint* ap;
+ char sout_char;
+ do {
+ sout_char = sout[sout_scan++];
+ if (escape) {
+ escape = false;
+ if (sout_char != '\0') {
+ wlan[section][wlan_put++] = sout_char;
+ continue;
+ }
+ }
+
+ if (sout_char == '\\') {
+ escape = true;
+ continue;
+ }
+
+ if (sout_char == ':') {
+ wlan[section][wlan_put] = '\0';
+ section++;
+ wlan_put = 0;
+ continue;
+ }
+
+ if ((sout_char == '\0') || (sout_char == '\n')) {
+ wlan[section][wlan_put] = '\0';
+ if (section == DLADM_SECTIONS - 1) {
+ ap = do_parse_str(wlan[0], wlan[1], wlan[2]);
+ if (ap) {
+ accessPoints.AppendObject(ap);
+ }
+ }
+ section = 0;
+ wlan_put = 0;
+ continue;
+ }
+
+ wlan[section][wlan_put++] = sout_char;
+
+ } while ((wlan_put <= DLADM_STRSIZE) && (section < DLADM_SECTIONS) &&
+ (sout_char != '\0'));
+ }
+
+ g_free(sout);
+ g_free(serr);
+}
+
+nsresult nsWifiMonitor::DoScan() {
+ // Regularly get the access point data.
+
+ nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
+ nsCOMArray<nsWifiAccessPoint> accessPoints;
+
+ while (mKeepGoing) {
+ accessPoints.Clear();
+ do_dladm(accessPoints);
+
+ bool accessPointsChanged =
+ !AccessPointsEqual(accessPoints, lastAccessPoints);
+ ReplaceArray(lastAccessPoints, accessPoints);
+
+ nsresult rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LOG(("waiting on monitor\n"));
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
+ }
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/nsWifiScannerWin.cpp b/netwerk/wifi/nsWifiScannerWin.cpp
new file mode 100644
index 0000000000..dba286f516
--- /dev/null
+++ b/netwerk/wifi/nsWifiScannerWin.cpp
@@ -0,0 +1,64 @@
+/* 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 "nsWifiMonitor.h"
+
+// moz headers (alphabetical)
+#include "nsCOMArray.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsWifiAccessPoint.h"
+#include "win_wifiScanner.h"
+
+using namespace mozilla;
+
+/**
+ * `nsWifiMonitor` is declared in the cross-platform nsWifiMonitor.h and
+ * is mostly defined in the cross-platform nsWifiMonitor.cpp. This function
+ * is implemented in various platform-specific files but the implementation
+ * is almost identical in each file. We relegate the Windows-specific
+ * work to the `WinWifiScanner` class and deal with non-Windows-specific
+ * issues like calling listeners here. Hopefully this file can be merged
+ * with the other implementations of `nsWifiMonitor::DoScan` since a lot
+ * of the code is identical
+ */
+nsresult nsWifiMonitor::DoScan() {
+ if (!mWinWifiScanner) {
+ mWinWifiScanner = MakeUnique<WinWifiScanner>();
+ if (!mWinWifiScanner) {
+ // TODO: Probably return OOM error
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Regularly get the access point data.
+
+ nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
+ nsCOMArray<nsWifiAccessPoint> accessPoints;
+
+ do {
+ accessPoints.Clear();
+ nsresult rv = mWinWifiScanner->GetAccessPointsFromWLAN(accessPoints);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool accessPointsChanged =
+ !AccessPointsEqual(accessPoints, lastAccessPoints);
+ ReplaceArray(lastAccessPoints, accessPoints);
+
+ rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // wait for some reasonable amount of time. pref?
+ LOG(("waiting on monitor\n"));
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mKeepGoing) {
+ mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
+ }
+ } while (mKeepGoing);
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/osx_corewlan.mm b/netwerk/wifi/osx_corewlan.mm
new file mode 100644
index 0000000000..71ef2c9ec2
--- /dev/null
+++ b/netwerk/wifi/osx_corewlan.mm
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import <Cocoa/Cocoa.h>
+#import <CoreWLAN/CoreWLAN.h>
+
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include <objc/objc.h>
+#include <objc/objc-runtime.h>
+
+#include "nsObjCExceptions.h"
+#include "nsCOMArray.h"
+#include "nsWifiMonitor.h"
+#include "nsWifiAccessPoint.h"
+
+nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint>& accessPoints) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ accessPoints.Clear();
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ @try {
+ NSBundle* bundle = [[[NSBundle alloc]
+ initWithPath:@"/System/Library/Frameworks/CoreWLAN.framework"] autorelease];
+ if (!bundle) {
+ [pool release];
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ Class CWI_class = [bundle classNamed:@"CWInterface"];
+ if (!CWI_class) {
+ [pool release];
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ id scanResult = [[CWI_class interface] scanForNetworksWithSSID:nil error:nil];
+ if (!scanResult) {
+ [pool release];
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ NSArray* scan = [NSMutableArray arrayWithArray:scanResult];
+ NSEnumerator* enumerator = [scan objectEnumerator];
+
+ while (id anObject = [enumerator nextObject]) {
+ auto* ap = new nsWifiAccessPoint();
+ if (!ap) {
+ [pool release];
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // [CWInterface bssidData] is deprecated on OS X 10.7 and up. Which is
+ // is a pain, so we'll use it for as long as it's available.
+ unsigned char macData[6] = {0};
+ if ([anObject respondsToSelector:@selector(bssidData)]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-method-access"
+ NSData* data = [anObject bssidData];
+#pragma clang diagnostic pop
+ if (data) {
+ memcpy(macData, [data bytes], 6);
+ }
+ } else {
+ // [CWInterface bssid] returns a string formatted "00:00:00:00:00:00".
+ NSString* macString = [anObject bssid];
+ if (macString && ([macString length] == 17)) {
+ for (NSUInteger i = 0; i < 6; ++i) {
+ NSString* part = [macString substringWithRange:NSMakeRange(i * 3, 2)];
+ NSScanner* scanner = [NSScanner scannerWithString:part];
+ unsigned int data = 0;
+ if (![scanner scanHexInt:&data]) {
+ data = 0;
+ }
+ macData[i] = (unsigned char)data;
+ }
+ }
+ }
+
+ int signal = (int)((NSInteger)[anObject rssiValue]);
+ ap->setMac(macData);
+ ap->setSignal(signal);
+ ap->setSSID([[anObject ssid] UTF8String], 32);
+
+ accessPoints.AppendObject(ap);
+ }
+ } @catch (NSException*) {
+ [pool release];
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ [pool release];
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_NOT_AVAILABLE);
+}
diff --git a/netwerk/wifi/osx_wifi.h b/netwerk/wifi/osx_wifi.h
new file mode 100644
index 0000000000..3a177f9643
--- /dev/null
+++ b/netwerk/wifi/osx_wifi.h
@@ -0,0 +1,119 @@
+// Copyright 2008, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The contents of this file are taken from Apple80211.h from the iStumbler
+// project (http://www.istumbler.net). This project is released under the BSD
+// license with the following restrictions.
+//
+// Copyright (c) 02006, Alf Watt (alf@istumbler.net). All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// * Neither the name of iStumbler nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This is the reverse engineered header for the Apple80211 private framework.
+// The framework can be found at
+// /System/Library/PrivateFrameworks/Apple80211.framework.
+
+#ifndef GEARS_GEOLOCATION_OSX_WIFI_H__
+#define GEARS_GEOLOCATION_OSX_WIFI_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+extern "C" {
+
+typedef SInt32 WIErr;
+
+// A WirelessContext should be created using WirelessAttach
+// before any other Wireless functions are called. WirelessDetach
+// is used to dispose of a WirelessContext.
+typedef struct __WirelessContext* WirelessContextPtr;
+
+// WirelessAttach
+//
+// This should be called before all other wireless functions.
+typedef WIErr (*WirelessAttachFunction)(WirelessContextPtr* outContext,
+ const UInt32);
+
+// WirelessDetach
+//
+// This should be called after all other wireless functions.
+typedef WIErr (*WirelessDetachFunction)(WirelessContextPtr inContext);
+
+typedef UInt16 WINetworkInfoFlags;
+
+struct WirelessNetworkInfo {
+ UInt16 channel; // Channel for the network.
+ SInt16 noise; // Noise for the network. 0 for Adhoc.
+ SInt16 signal; // Signal strength of the network. 0 for Adhoc.
+ UInt8 macAddress[6]; // MAC address of the wireless access point.
+ UInt16 beaconInterval; // Beacon interval in milliseconds
+ WINetworkInfoFlags flags; // Flags for the network
+ UInt16 nameLen;
+ SInt8 name[32];
+};
+
+// WirelessScanSplit
+//
+// WirelessScanSplit scans for available wireless networks. It will allocate 2
+// CFArrays to store a list of managed and adhoc networks. The arrays hold
+// CFData objects which contain WirelessNetworkInfo structures.
+//
+// Note: An adhoc network created on the computer the scan is running on will
+// not be found. WirelessGetInfo can be used to find info about a local adhoc
+// network.
+//
+// If stripDups != 0 only one bases tation for each SSID will be returned.
+typedef WIErr (*WirelessScanSplitFunction)(WirelessContextPtr inContext,
+ CFArrayRef* apList,
+ CFArrayRef* adhocList,
+ const UInt32 stripDups);
+
+} // extern "C"
+
+#endif // GEARS_GEOLOCATION_OSX_WIFI_H__
diff --git a/netwerk/wifi/win_wifiScanner.cpp b/netwerk/wifi/win_wifiScanner.cpp
new file mode 100644
index 0000000000..35fe859327
--- /dev/null
+++ b/netwerk/wifi/win_wifiScanner.cpp
@@ -0,0 +1,169 @@
+/* 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 "nsWifiAccessPoint.h"
+#include "win_wifiScanner.h"
+
+// Moz headers (alphabetical)
+#include "win_wlanLibrary.h"
+
+#define DOT11_BSS_TYPE_UNUSED static_cast<DOT11_BSS_TYPE>(0)
+
+class InterfaceScanCallbackData {
+ public:
+ explicit InterfaceScanCallbackData(uint32_t numInterfaces)
+ : mCurrentlyScanningInterfaces(numInterfaces) {
+ mAllInterfacesDoneScanningEvent =
+ ::CreateEventW(nullptr, // null security
+ TRUE, // manual reset event
+ FALSE, // initially nonsignaled
+ nullptr); // not named
+ MOZ_ASSERT(NULL != mAllInterfacesDoneScanningEvent);
+ }
+
+ ~InterfaceScanCallbackData() {
+ ::CloseHandle(mAllInterfacesDoneScanningEvent);
+ }
+
+ void OnInterfaceScanComplete() {
+ uint32_t val = ::InterlockedDecrement(&mCurrentlyScanningInterfaces);
+ if (!val) {
+ ::SetEvent(mAllInterfacesDoneScanningEvent);
+ }
+ }
+
+ void WaitForAllInterfacesToFinishScanning(uint32_t msToWait) {
+ ::WaitForSingleObject(mAllInterfacesDoneScanningEvent, msToWait);
+ }
+
+ private:
+ volatile uint32_t mCurrentlyScanningInterfaces;
+ HANDLE mAllInterfacesDoneScanningEvent;
+};
+
+static void WINAPI OnScanComplete(PWLAN_NOTIFICATION_DATA data, PVOID context) {
+ if (WLAN_NOTIFICATION_SOURCE_ACM != data->NotificationSource) {
+ return;
+ }
+
+ if (wlan_notification_acm_scan_complete != data->NotificationCode &&
+ wlan_notification_acm_scan_fail != data->NotificationCode) {
+ return;
+ }
+
+ InterfaceScanCallbackData* cbData =
+ reinterpret_cast<InterfaceScanCallbackData*>(context);
+ cbData->OnInterfaceScanComplete();
+}
+
+WinWifiScanner::WinWifiScanner() {
+ // NOTE: We assume that, if we were unable to load the WLAN library when
+ // we initially tried, we will not be able to load it in the future.
+ // Technically, on Windows XP SP2, a user could install the redistributable
+ // and make our assumption incorrect. We opt to avoid making a bunch of
+ // spurious LoadLibrary calls in the common case rather than load the
+ // WLAN API in the edge case.
+ mWlanLibrary.reset(WinWLANLibrary::Load());
+ if (!mWlanLibrary) {
+ NS_WARNING("Could not initialize Windows Wi-Fi scanner");
+ }
+}
+
+WinWifiScanner::~WinWifiScanner() {}
+
+nsresult WinWifiScanner::GetAccessPointsFromWLAN(
+ nsCOMArray<nsWifiAccessPoint>& accessPoints) {
+ accessPoints.Clear();
+
+ // NOTE: We do not try to load the WLAN library if we previously failed
+ // to load it. See the note in WinWifiScanner constructor
+ if (!mWlanLibrary) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Get the list of interfaces. WlanEnumInterfaces allocates interface_list.
+ WLAN_INTERFACE_INFO_LIST* interface_list = nullptr;
+ if (ERROR_SUCCESS !=
+ (*mWlanLibrary->GetWlanEnumInterfacesPtr())(mWlanLibrary->GetWLANHandle(),
+ nullptr, &interface_list)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // This ensures we call WlanFreeMemory on interface_list
+ ScopedWLANObject scopedInterfaceList(*mWlanLibrary, interface_list);
+
+ if (!interface_list->dwNumberOfItems) {
+ return NS_OK;
+ }
+
+ InterfaceScanCallbackData cbData(interface_list->dwNumberOfItems);
+
+ DWORD wlanNotifySource;
+ if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanRegisterNotificationPtr())(
+ mWlanLibrary->GetWLANHandle(),
+ WLAN_NOTIFICATION_SOURCE_ACM, TRUE,
+ (WLAN_NOTIFICATION_CALLBACK)OnScanComplete, &cbData,
+ NULL, &wlanNotifySource)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Go through the list of interfaces and call `WlanScan` on each
+ for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i) {
+ if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanScanPtr())(
+ mWlanLibrary->GetWLANHandle(),
+ &interface_list->InterfaceInfo[i].InterfaceGuid,
+ NULL, NULL, NULL)) {
+ cbData.OnInterfaceScanComplete();
+ }
+ }
+
+ // From the MSDN documentation:
+ // "Wireless network drivers that meet Windows logo requirements are
+ // required to complete a WlanScan function request in 4 seconds"
+ cbData.WaitForAllInterfacesToFinishScanning(5000);
+
+ // Unregister for the notifications. The documentation mentions that,
+ // if a callback is currently running, this will wait for the callback
+ // to complete.
+ (*mWlanLibrary->GetWlanRegisterNotificationPtr())(
+ mWlanLibrary->GetWLANHandle(), WLAN_NOTIFICATION_SOURCE_NONE, TRUE, NULL,
+ NULL, NULL, &wlanNotifySource);
+
+ // Go through the list of interfaces and get the data for each.
+ for (uint32_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
+ WLAN_BSS_LIST* bss_list;
+ if (ERROR_SUCCESS != (*mWlanLibrary->GetWlanGetNetworkBssListPtr())(
+ mWlanLibrary->GetWLANHandle(),
+ &interface_list->InterfaceInfo[i].InterfaceGuid,
+ nullptr, // Use all SSIDs.
+ DOT11_BSS_TYPE_UNUSED,
+ false, // bSecurityEnabled -
+ // unused
+ nullptr, // reserved
+ &bss_list)) {
+ continue;
+ }
+
+ // This ensures we call WlanFreeMemory on bss_list
+ ScopedWLANObject scopedBssList(*mWlanLibrary, bss_list);
+
+ // Store each discovered access point in our outparam
+ for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) {
+ nsWifiAccessPoint* ap = new nsWifiAccessPoint();
+ if (!ap) {
+ continue;
+ }
+
+ const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
+ ap->setMac(bss_entry.dot11Bssid);
+ ap->setSignal(bss_entry.lRssi);
+ ap->setSSID(reinterpret_cast<char const*>(bss_entry.dot11Ssid.ucSSID),
+ bss_entry.dot11Ssid.uSSIDLength);
+
+ accessPoints.AppendObject(ap);
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/netwerk/wifi/win_wifiScanner.h b/netwerk/wifi/win_wifiScanner.h
new file mode 100644
index 0000000000..d19bda6fc3
--- /dev/null
+++ b/netwerk/wifi/win_wifiScanner.h
@@ -0,0 +1,32 @@
+/* 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/. */
+
+#pragma once
+
+// Moz headers (alphabetical)
+#include "mozilla/UniquePtr.h"
+#include "nsCOMArray.h"
+#include "win_wlanLibrary.h"
+
+class nsWifiAccessPoint;
+
+class WinWifiScanner final {
+ public:
+ WinWifiScanner();
+ ~WinWifiScanner();
+
+ /**
+ * GetAccessPointsFromWLAN
+ *
+ * Scans the available wireless interfaces for nearby access points and
+ * populates the supplied collection with them
+ *
+ * @param accessPoints The collection to populate with available APs
+ * @return NS_OK on success, failure codes on failure
+ */
+ nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint>& accessPoints);
+
+ private:
+ mozilla::UniquePtr<WinWLANLibrary> mWlanLibrary;
+};
diff --git a/netwerk/wifi/win_wlanLibrary.cpp b/netwerk/wifi/win_wlanLibrary.cpp
new file mode 100644
index 0000000000..8e5ad00b0c
--- /dev/null
+++ b/netwerk/wifi/win_wlanLibrary.cpp
@@ -0,0 +1,122 @@
+/* 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 "win_wlanLibrary.h"
+
+// Moz headers (alphabetical)
+
+WinWLANLibrary* WinWLANLibrary::Load() {
+ WinWLANLibrary* ret = new WinWLANLibrary();
+ if (!ret) {
+ return nullptr;
+ }
+
+ if (!ret->Initialize()) {
+ delete ret;
+ return nullptr;
+ }
+
+ return ret;
+}
+
+WinWLANLibrary::WinWLANLibrary()
+ : mWlanLibrary(nullptr),
+ mWlanHandle(nullptr),
+ mWlanEnumInterfacesPtr(nullptr),
+ mWlanGetNetworkBssListPtr(nullptr),
+ mWlanFreeMemoryPtr(nullptr),
+ mWlanCloseHandlePtr(nullptr),
+ mWlanOpenHandlePtr(nullptr),
+ mWlanRegisterNotificationPtr(nullptr),
+ mWlanScanPtr(nullptr) {}
+
+HANDLE
+WinWLANLibrary::GetWLANHandle() const { return mWlanHandle; }
+
+decltype(::WlanEnumInterfaces)* WinWLANLibrary::GetWlanEnumInterfacesPtr()
+ const {
+ return mWlanEnumInterfacesPtr;
+}
+
+decltype(::WlanGetNetworkBssList)* WinWLANLibrary::GetWlanGetNetworkBssListPtr()
+ const {
+ return mWlanGetNetworkBssListPtr;
+}
+
+decltype(::WlanFreeMemory)* WinWLANLibrary::GetWlanFreeMemoryPtr() const {
+ return mWlanFreeMemoryPtr;
+}
+
+decltype(::WlanCloseHandle)* WinWLANLibrary::GetWlanCloseHandlePtr() const {
+ return mWlanCloseHandlePtr;
+}
+
+decltype(::WlanOpenHandle)* WinWLANLibrary::GetWlanOpenHandlePtr() const {
+ return mWlanOpenHandlePtr;
+}
+
+decltype(::WlanRegisterNotification)*
+WinWLANLibrary::GetWlanRegisterNotificationPtr() const {
+ return mWlanRegisterNotificationPtr;
+}
+
+decltype(::WlanScan)* WinWLANLibrary::GetWlanScanPtr() const {
+ return mWlanScanPtr;
+}
+
+bool WinWLANLibrary::Initialize() {
+ mWlanLibrary = LoadLibraryW(L"Wlanapi.dll");
+ if (!mWlanLibrary) {
+ return false;
+ }
+
+ mWlanOpenHandlePtr = (decltype(::WlanOpenHandle)*)GetProcAddress(
+ mWlanLibrary, "WlanOpenHandle");
+ mWlanEnumInterfacesPtr = (decltype(::WlanEnumInterfaces)*)GetProcAddress(
+ mWlanLibrary, "WlanEnumInterfaces");
+ mWlanRegisterNotificationPtr =
+ (decltype(::WlanRegisterNotification)*)GetProcAddress(
+ mWlanLibrary, "WlanRegisterNotification");
+ mWlanScanPtr =
+ (decltype(::WlanScan)*)GetProcAddress(mWlanLibrary, "WlanScan");
+
+ mWlanFreeMemoryPtr = (decltype(::WlanFreeMemory)*)GetProcAddress(
+ mWlanLibrary, "WlanFreeMemory");
+ mWlanCloseHandlePtr = (decltype(::WlanCloseHandle)*)GetProcAddress(
+ mWlanLibrary, "WlanCloseHandle");
+ mWlanGetNetworkBssListPtr =
+ (decltype(::WlanGetNetworkBssList)*)GetProcAddress(
+ mWlanLibrary, "WlanGetNetworkBssList");
+
+ if (!mWlanOpenHandlePtr || !mWlanEnumInterfacesPtr ||
+ !mWlanRegisterNotificationPtr || !mWlanGetNetworkBssListPtr ||
+ !mWlanScanPtr || !mWlanFreeMemoryPtr || !mWlanCloseHandlePtr) {
+ return false;
+ }
+
+ // Get the handle to the WLAN API.
+ DWORD negotiated_version;
+ // We could be executing on either Windows XP or Windows Vista, so use the
+ // lower version of the client WLAN API. It seems that the negotiated version
+ // is the Vista version irrespective of what we pass!
+ static const int kXpWlanClientVersion = 1;
+ if (ERROR_SUCCESS != (*mWlanOpenHandlePtr)(kXpWlanClientVersion, nullptr,
+ &negotiated_version,
+ &mWlanHandle)) {
+ return false;
+ }
+
+ return true;
+}
+
+WinWLANLibrary::~WinWLANLibrary() {
+ if (mWlanLibrary) {
+ if (mWlanHandle) {
+ (*mWlanCloseHandlePtr)(mWlanLibrary, mWlanHandle);
+ mWlanHandle = nullptr;
+ }
+ ::FreeLibrary(mWlanLibrary);
+ mWlanLibrary = nullptr;
+ }
+}
diff --git a/netwerk/wifi/win_wlanLibrary.h b/netwerk/wifi/win_wlanLibrary.h
new file mode 100644
index 0000000000..864e3f71a3
--- /dev/null
+++ b/netwerk/wifi/win_wlanLibrary.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sts=2 sw=2 et cin: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+// Moz headers (alphabetical)
+
+// System headers (alphabetical)
+#include <windows.h> // HINSTANCE, HANDLE
+#include <wlanapi.h> // Wlan* functions
+
+class WinWLANLibrary {
+ public:
+ static WinWLANLibrary* Load();
+ ~WinWLANLibrary();
+
+ HANDLE GetWLANHandle() const;
+ decltype(::WlanEnumInterfaces)* GetWlanEnumInterfacesPtr() const;
+ decltype(::WlanGetNetworkBssList)* GetWlanGetNetworkBssListPtr() const;
+ decltype(::WlanFreeMemory)* GetWlanFreeMemoryPtr() const;
+ decltype(::WlanCloseHandle)* GetWlanCloseHandlePtr() const;
+ decltype(::WlanOpenHandle)* GetWlanOpenHandlePtr() const;
+ decltype(::WlanRegisterNotification)* GetWlanRegisterNotificationPtr() const;
+ decltype(::WlanScan)* GetWlanScanPtr() const;
+
+ private:
+ WinWLANLibrary();
+ bool Initialize();
+
+ HMODULE mWlanLibrary;
+ HANDLE mWlanHandle;
+ decltype(::WlanEnumInterfaces)* mWlanEnumInterfacesPtr;
+ decltype(::WlanGetNetworkBssList)* mWlanGetNetworkBssListPtr;
+ decltype(::WlanFreeMemory)* mWlanFreeMemoryPtr;
+ decltype(::WlanCloseHandle)* mWlanCloseHandlePtr;
+ decltype(::WlanOpenHandle)* mWlanOpenHandlePtr;
+ decltype(::WlanRegisterNotification)* mWlanRegisterNotificationPtr;
+ decltype(::WlanScan)* mWlanScanPtr;
+};
+
+class ScopedWLANObject {
+ public:
+ ScopedWLANObject(const WinWLANLibrary& library, void* object)
+ : mLibrary(library), mObject(object) {}
+
+ ~ScopedWLANObject() { (*(mLibrary.GetWlanFreeMemoryPtr()))(mObject); }
+
+ private:
+ const WinWLANLibrary& mLibrary;
+ void* mObject;
+};