diff options
Diffstat (limited to 'netwerk/wifi')
-rw-r--r-- | netwerk/wifi/moz.build | 53 | ||||
-rw-r--r-- | netwerk/wifi/nsIWifiAccessPoint.idl | 41 | ||||
-rw-r--r-- | netwerk/wifi/nsIWifiListener.idl | 29 | ||||
-rw-r--r-- | netwerk/wifi/nsIWifiMonitor.idl | 24 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiAccessPoint.cpp | 81 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiAccessPoint.h | 79 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiMonitor.cpp | 258 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiMonitor.h | 83 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerDBus.cpp | 375 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerDBus.h | 44 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerFreeBSD.cpp | 168 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerMac.cpp | 51 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerSolaris.cpp | 144 | ||||
-rw-r--r-- | netwerk/wifi/nsWifiScannerWin.cpp | 64 | ||||
-rw-r--r-- | netwerk/wifi/osx_corewlan.mm | 100 | ||||
-rw-r--r-- | netwerk/wifi/osx_wifi.h | 119 | ||||
-rw-r--r-- | netwerk/wifi/win_wifiScanner.cpp | 169 | ||||
-rw-r--r-- | netwerk/wifi/win_wifiScanner.h | 32 | ||||
-rw-r--r-- | netwerk/wifi/win_wlanLibrary.cpp | 122 | ||||
-rw-r--r-- | netwerk/wifi/win_wlanLibrary.h | 54 |
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, + ¶mInterface)) { + return NS_ERROR_FAILURE; + } + + const char* paramDeviceType = "DeviceType"; + if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, + ¶mDeviceType)) { + 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, ¶m)) { + 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; +}; |