diff options
Diffstat (limited to 'netwerk/wifi')
24 files changed, 2894 insertions, 0 deletions
diff --git a/netwerk/wifi/moz.build b/netwerk/wifi/moz.build new file mode 100644 index 000000000..b6ddd2139 --- /dev/null +++ b/netwerk/wifi/moz.build @@ -0,0 +1,65 @@ +# -*- 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', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': + UNIFIED_SOURCES += [ + 'nsWifiMonitorGonk.cpp', + ] +else: + UNIFIED_SOURCES += [ + 'nsWifiMonitor.cpp', + ] + +if CONFIG['OS_ARCH'] == 'Darwin': + UNIFIED_SOURCES += [ + 'nsWifiScannerMac.cpp', + ] + SOURCES += [ + 'osx_corewlan.mm', + ] + # osx_corewlan.mm has warnings about scanForNetworksWithParameters, + # bssidData and rssi. These are APIs that were removed in 10.7, so we need + # to accept the warnings when targeting the newer SDKs. + SOURCES['osx_corewlan.mm'].flags += ['-Wno-error=objc-method-access'] +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', + 'win_xp_wifiScanner.cpp' + ] +elif CONFIG['OS_ARCH'] == 'SunOS': + CXXFLAGS += CONFIG['GLIB_CFLAGS'] + UNIFIED_SOURCES += [ + 'nsWifiScannerSolaris.cpp', + ] + +if CONFIG['NECKO_WIFI_DBUS']: + UNIFIED_SOURCES += [ + 'nsWifiScannerDBus.cpp', + ] + CXXFLAGS += ['-Wno-error=shadow'] + +if CONFIG['NECKO_WIFI_DBUS']: + CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS'] + +FINAL_LIBRARY = 'xul' diff --git a/netwerk/wifi/nsIWifiAccessPoint.idl b/netwerk/wifi/nsIWifiAccessPoint.idl new file mode 100644 index 000000000..c98b42edc --- /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 000000000..ffac02654 --- /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([array, size_is(aLen)] in nsIWifiAccessPoint accessPoints, in unsigned long aLen); + + /* + * 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 000000000..aaa318d25 --- /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 000000000..995afaba5 --- /dev/null +++ b/netwerk/wifi/nsWifiAccessPoint.cpp @@ -0,0 +1,95 @@ +/* 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 "nsMemory.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; +} + +nsWifiAccessPoint::~nsWifiAccessPoint() +{ +} + +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. + CopyASCIItoUTF16(mSsid, aSsid); + 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 000000000..4bfb7c175 --- /dev/null +++ b/netwerk/wifi/nsWifiAccessPoint.h @@ -0,0 +1,86 @@ +/* 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(); + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIWIFIACCESSPOINT + + nsWifiAccessPoint(); + + char mMac[18]; + int mSignal; + char mSsid[33]; + 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 000000000..4613a107d --- /dev/null +++ b/netwerk/wifi/nsWifiMonitor.cpp @@ -0,0 +1,265 @@ +/* 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/Services.h" + +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")); +} + +nsWifiMonitor::~nsWifiMonitor() +{ +} + +NS_IMETHODIMP +nsWifiMonitor::Observe(nsISupports *subject, const char *topic, + const char16_t *data) +{ + if (!strcmp(topic, "xpcom-shutdown")) { + LOG(("Shutting down\n")); + + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + mKeepGoing = false; + mon.Notify(); + mThread = nullptr; + } + return NS_OK; +} + + +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_NewThread(getter_AddRefs(mThread), this); + if (NS_FAILED(rv)) + return rv; + } + + + mListeners.AppendElement(nsWifiListener(new nsMainThreadPtrHolder<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; +} + +typedef nsTArray<nsMainThreadPtrHandle<nsIWifiListener> > WifiListenerArray; + +class nsPassErrorToWifiListeners final : public nsIRunnable +{ + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + + nsPassErrorToWifiListeners(nsAutoPtr<WifiListenerArray> aListeners, + nsresult aResult) + : mListeners(aListeners), + mResult(aResult) + {} + + private: + ~nsPassErrorToWifiListeners() {} + nsAutoPtr<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")); + + PR_SetCurrentThreadName("Wifi Monitor"); + + nsresult rv = DoScan(); + LOG(("@@@@@ wifi monitor run::doscan complete %x\n", rv)); + + nsAutoPtr<WifiListenerArray> currentListeners; + bool doError = false; + + { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + if (mKeepGoing && NS_FAILED(rv)) { + doError = true; + currentListeners = new WifiListenerArray(mListeners.Length()); + for (uint32_t i = 0; i < mListeners.Length(); i++) + currentListeners->AppendElement(mListeners[i].mListener); + } + mThreadComplete = true; + } + + if (doError) { + nsCOMPtr<nsIThread> thread = do_GetMainThread(); + if (!thread) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIRunnable> runnable(new nsPassErrorToWifiListeners(currentListeners, rv)); + if (!runnable) + return NS_ERROR_OUT_OF_MEMORY; + + thread->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(nsAutoPtr<WifiListenerArray> aListeners, + nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > aAccessPoints) + : mListeners(aListeners), + mAccessPoints(aAccessPoints) + {} + + private: + ~nsCallWifiListeners() {} + nsAutoPtr<WifiListenerArray> mListeners; + nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > mAccessPoints; +}; + +NS_IMPL_ISUPPORTS(nsCallWifiListeners, + nsIRunnable) + +NS_IMETHODIMP nsCallWifiListeners::Run() +{ + LOG(("About to send data to the wifi listeners\n")); + for (size_t i = 0; i < mListeners->Length(); i++) { + (*mListeners)[i]->OnChange(mAccessPoints->Elements(), mAccessPoints->Length()); + } + return NS_OK; +} + +nsresult +nsWifiMonitor::CallWifiListeners(const nsCOMArray<nsWifiAccessPoint> &aAccessPoints, + bool aAccessPointsChanged) +{ + nsAutoPtr<WifiListenerArray> currentListeners; + { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + currentListeners = new WifiListenerArray(mListeners.Length()); + + for (uint32_t i = 0; i < mListeners.Length(); i++) { + if (!mListeners[i].mHasSentData || aAccessPointsChanged) { + mListeners[i].mHasSentData = true; + currentListeners->AppendElement(mListeners[i].mListener); + } + } + } + + if (currentListeners->Length() > 0) + { + uint32_t resultCount = aAccessPoints.Count(); + nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > accessPoints( + new nsTArray<nsIWifiAccessPoint *>(resultCount)); + if (!accessPoints) + return NS_ERROR_OUT_OF_MEMORY; + + 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(currentListeners, 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 000000000..3783d38bd --- /dev/null +++ b/netwerk/wifi/nsWifiMonitor.h @@ -0,0 +1,110 @@ +/* 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 "nsAutoPtr.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 "nsITimer.h" +#include "mozilla/Attributes.h" +#include "nsIInterfaceRequestor.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 */ + +class nsWifiListener +{ + public: + + explicit nsWifiListener(nsMainThreadPtrHolder<nsIWifiListener>* aListener) + { + mListener = aListener; + mHasSentData = false; + } + ~nsWifiListener() {} + + nsMainThreadPtrHandle<nsIWifiListener> mListener; + bool mHasSentData; +}; + +#ifndef MOZ_WIDGET_GONK +class nsWifiMonitor final : nsIRunnable, nsIWifiMonitor, nsIObserver +{ + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIWIFIMONITOR + NS_DECL_NSIRUNNABLE + NS_DECL_NSIOBSERVER + + nsWifiMonitor(); + + private: + ~nsWifiMonitor(); + + nsresult DoScan(); + + nsresult CallWifiListeners(const nsCOMArray<nsWifiAccessPoint> &aAccessPoints, + bool aAccessPointsChanged); + + mozilla::Atomic<bool> mKeepGoing; + mozilla::Atomic<bool> mThreadComplete; + nsCOMPtr<nsIThread> mThread; + + nsTArray<nsWifiListener> mListeners; + + mozilla::ReentrantMonitor mReentrantMonitor; + +#ifdef XP_WIN + nsAutoPtr<WindowsWifiScannerInterface> mWinWifiScanner; +#endif +}; +#else +#include "nsIWifi.h" +class nsWifiMonitor final : nsIWifiMonitor, nsIWifiScanResultsReady, nsIObserver +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWIFIMONITOR + NS_DECL_NSIOBSERVER + NS_DECL_NSIWIFISCANRESULTSREADY + + nsWifiMonitor(); + + private: + ~nsWifiMonitor(); + + void ClearTimer() { + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } + } + void StartScan(); + nsCOMArray<nsWifiAccessPoint> mLastAccessPoints; + nsTArray<nsWifiListener> mListeners; + nsCOMPtr<nsITimer> mTimer; +}; +#endif + +#endif diff --git a/netwerk/wifi/nsWifiMonitorGonk.cpp b/netwerk/wifi/nsWifiMonitorGonk.cpp new file mode 100644 index 000000000..017750549 --- /dev/null +++ b/netwerk/wifi/nsWifiMonitorGonk.cpp @@ -0,0 +1,181 @@ +/* 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 "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/Services.h" + +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" + +using namespace mozilla; + +LazyLogModule gWifiMonitorLog("WifiMonitor"); + +NS_IMPL_ISUPPORTS(nsWifiMonitor, + nsIWifiMonitor, + nsIObserver, + nsIWifiScanResultsReady) + +nsWifiMonitor::nsWifiMonitor() +{ + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->AddObserver(this, "xpcom-shutdown", false); + } + LOG(("@@@@@ wifimonitor created\n")); +} + +nsWifiMonitor::~nsWifiMonitor() +{ +} + +NS_IMETHODIMP +nsWifiMonitor::StartWatching(nsIWifiListener *aListener) +{ + LOG(("@@@@@ nsWifiMonitor::StartWatching\n")); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (!aListener) { + return NS_ERROR_NULL_POINTER; + } + + mListeners.AppendElement(nsWifiListener(new nsMainThreadPtrHolder<nsIWifiListener>(aListener))); + + if (!mTimer) { + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + mTimer->Init(this, 5000, nsITimer::TYPE_REPEATING_SLACK); + } + StartScan(); + return NS_OK; +} + +NS_IMETHODIMP +nsWifiMonitor::StopWatching(nsIWifiListener *aListener) +{ + LOG(("@@@@@ nsWifiMonitor::StopWatching\n")); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (!aListener) { + return NS_ERROR_NULL_POINTER; + } + + for (uint32_t i = 0; i < mListeners.Length(); i++) { + if (mListeners[i].mListener == aListener) { + mListeners.RemoveElementAt(i); + break; + } + } + + if (mListeners.Length() == 0) { + ClearTimer(); + } + return NS_OK; +} + +void +nsWifiMonitor::StartScan() +{ + nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1"); + nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir); + if (!wifi) { + return; + } + wifi->GetWifiScanResults(this); +} + +NS_IMETHODIMP +nsWifiMonitor::Observe(nsISupports *subject, const char *topic, + const char16_t *data) +{ + if (!strcmp(topic, "timer-callback")) { + LOG(("timer callback\n")); + StartScan(); + return NS_OK; + } + + if (!strcmp(topic, "xpcom-shutdown")) { + LOG(("Shutting down\n")); + ClearTimer(); + return NS_OK; + } + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsWifiMonitor::Onready(uint32_t count, nsIWifiScanResult **results) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + LOG(("@@@@@ About to send data to the wifi listeners\n")); + + nsCOMArray<nsWifiAccessPoint> accessPoints; + + for (uint32_t i = 0; i < count; i++) { + RefPtr<nsWifiAccessPoint> ap = new nsWifiAccessPoint(); + + nsString temp; + results[i]->GetBssid(temp); + // 00:00:00:00:00:00 --> 00-00-00-00-00-00 + for (int32_t x=0; x<6; x++) { + temp.ReplaceSubstring(NS_LITERAL_STRING(":"), NS_LITERAL_STRING("-")); // would it be too much to ask for a ReplaceAll()? + } + + nsCString mac; + mac.AssignWithConversion(temp); + + results[i]->GetSsid(temp); + + nsCString ssid; + ssid.AssignWithConversion(temp); + + uint32_t signal; + results[i]->GetSignalStrength(&signal); + + ap->setSignal(signal); + ap->setMacRaw(mac.get()); + ap->setSSIDRaw(ssid.get(), ssid.Length()); + + accessPoints.AppendObject(ap); + } + + bool accessPointsChanged = !AccessPointsEqual(accessPoints, mLastAccessPoints); + ReplaceArray(mLastAccessPoints, accessPoints); + + nsTArray<nsIWifiAccessPoint*> ac; + uint32_t resultCount = mLastAccessPoints.Count(); + for (uint32_t i = 0; i < resultCount; i++) { + ac.AppendElement(mLastAccessPoints[i]); + } + + for (uint32_t i = 0; i < mListeners.Length(); i++) { + if (!mListeners[i].mHasSentData || accessPointsChanged) { + mListeners[i].mHasSentData = true; + mListeners[i].mListener->OnChange(ac.Elements(), ac.Length()); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsWifiMonitor::Onfailure() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + LOG(("@@@@@ About to send error to the wifi listeners\n")); + for (uint32_t i = 0; i < mListeners.Length(); i++) { + mListeners[i].mListener->OnError(NS_ERROR_UNEXPECTED); + } + + ClearTimer(); + return NS_OK; +} diff --git a/netwerk/wifi/nsWifiScannerDBus.cpp b/netwerk/wifi/nsWifiScannerDBus.cpp new file mode 100644 index 000000000..182553e18 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerDBus.cpp @@ -0,0 +1,337 @@ +/* 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/ipc/DBusMessageRefPtr.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)); + MOZ_ASSERT(mConnection); + dbus_connection_set_exit_on_disconnect(mConnection, false); + + MOZ_COUNT_CTOR(nsWifiScannerDBus); +} + +nsWifiScannerDBus::~nsWifiScannerDBus() +{ + MOZ_COUNT_DTOR(nsWifiScannerDBus); +} + +nsresult +nsWifiScannerDBus::Scan() +{ + return SendMessage("org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "GetDevices"); +} + +nsresult +nsWifiScannerDBus::SendMessage(const char* aInterface, + const char* aPath, + const char* aFuncCall) +{ + RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>( + dbus_message_new_method_call("org.freedesktop.NetworkManager", + aPath, aInterface, aFuncCall)); + if (!msg) { + return NS_ERROR_FAILURE; + } + + DBusMessageIter argsIter; + dbus_message_iter_init_append(msg, &argsIter); + + if (!strcmp(aFuncCall, "Get")) { + 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; + } + } else if (!strcmp(aFuncCall, "GetAll")) { + const char* param = ""; + if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, ¶m)) { + return NS_ERROR_FAILURE; + } + } + + DBusError err; + dbus_error_init(&err); + + // http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html + // Refer to function dbus_connection_send_with_reply_and_block. + const uint32_t DBUS_DEFAULT_TIMEOUT = -1; + 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. + if (!strcmp(aFuncCall, "GetAccessPoints")) { + return NS_OK; + } + return NS_ERROR_FAILURE; + } + + nsresult rv; + if (!strcmp(aFuncCall, "GetDevices")) { + rv = IdentifyDevices(reply); + } else if (!strcmp(aFuncCall, "Get")) { + rv = IdentifyDeviceType(reply, aPath); + } else if (!strcmp(aFuncCall, "GetAccessPoints")) { + rv = IdentifyAccessPoints(reply); + } else if (!strcmp(aFuncCall, "GetAll")) { + rv = IdentifyAPProperties(reply); + } else { + rv = NS_ERROR_FAILURE; + } + return rv; +} + +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 = SendMessage("org.freedesktop.DBus.Properties", devicePath, "Get"); + 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 = SendMessage("org.freedesktop.NetworkManager.Device.Wireless", + aDevicePath, "GetAccessPoints"); + } + + 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 = SendMessage("org.freedesktop.DBus.Properties", path, "GetAll"); + 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; + dbus_message_iter_get_basic(&variant, &strength); + ap->setSignal(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* token = strtok(hwAddress, ":"); + for (uint32_t i = 0; i < ArrayLength(macAddress); i++) { + if (!token) { + return NS_ERROR_FAILURE; + } + macAddress[i] = strtoul(token, nullptr, 16); + token = strtok(nullptr, ":"); + } + 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; +} + +} // 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 000000000..516eb5ff2 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerDBus.h @@ -0,0 +1,45 @@ +/* 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 "mozilla/ipc/DBusConnectionRefPtr.h" + +class nsWifiAccessPoint; + +namespace mozilla { + +class nsWifiScannerDBus final +{ +public: + explicit nsWifiScannerDBus(nsCOMArray<nsWifiAccessPoint>* aAccessPoints); + ~nsWifiScannerDBus(); + + nsresult Scan(); + +private: + nsresult SendMessage(const char* aInterface, + const char* aPath, + const char* aFuncCall); + 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; +}; + +} // mozilla + +#endif // NSWIFIAPSCANNERDBUS_H_ diff --git a/netwerk/wifi/nsWifiScannerFreeBSD.cpp b/netwerk/wifi/nsWifiScannerFreeBSD.cpp new file mode 100644 index 000000000..4185d6989 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerFreeBSD.cpp @@ -0,0 +1,171 @@ +/* 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 000000000..b8ca682c2 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerMac.cpp @@ -0,0 +1,55 @@ +/* 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 "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "nsWifiMonitor.h" +#include "nsWifiAccessPoint.h" + +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.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 000000000..6fbfa0eb7 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerSolaris.cpp @@ -0,0 +1,149 @@ +/* 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 "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" + +#include "plstr.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 (!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); + ap->setSSID(essid_str, PL_strnlen(essid_str, DLADM_STRSIZE)); + } + 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 000000000..ef18706e4 --- /dev/null +++ b/netwerk/wifi/nsWifiScannerWin.cpp @@ -0,0 +1,77 @@ +/* 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 "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" +#include "nsServiceManagerUtils.h" +#include "nsWifiAccessPoint.h" +#include "win_wifiScanner.h" +#include "win_xp_wifiScanner.h" +#include "mozilla/WindowsVersion.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) { + if (IsWin2003OrLater()) { + mWinWifiScanner = new WinWifiScanner(); + LOG(("Using Windows 2003+ wifi scanner.")); + } else { + mWinWifiScanner = new WinXPWifiScanner(); + LOG(("Using Windows XP wifi scanner.")); + } + + 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 000000000..5cc96729a --- /dev/null +++ b/netwerk/wifi/osx_corewlan.mm @@ -0,0 +1,108 @@ +/* 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 "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "nsWifiMonitor.h" +#include "nsWifiAccessPoint.h" + +nsresult +GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints) +{ + NS_OBJC_BEGIN_TRY_ABORT_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] scanForNetworksWithParameters: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)]) { + NSData* data = [anObject bssidData]; + 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; + } + } + } + + // [CWInterface rssiValue] is available on OS X 10.7 and up (and + // [CWInterface rssi] is deprecated). + int signal = 0; + if ([anObject respondsToSelector:@selector(rssiValue)]) { + signal = (int) ((NSInteger) [anObject rssiValue]); + } else { + signal = [[anObject rssi] intValue]; + } + + 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_ABORT_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 000000000..8a72a84e2 --- /dev/null +++ b/netwerk/wifi/osx_wifi.h @@ -0,0 +1,120 @@ +// 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/tests/wifi_access_point_test.html b/netwerk/wifi/tests/wifi_access_point_test.html new file mode 100644 index 000000000..974a6be15 --- /dev/null +++ b/netwerk/wifi/tests/wifi_access_point_test.html @@ -0,0 +1,60 @@ +<html> +<head> +<title>hi</title> +<script> + +var count = 0; + + +function test() { +} + +test.prototype = +{ + onChange: function (accessPoints) + { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + var d = document.getElementById("d"); + d.innerHTML = ""; + + for (var i=0; i<accessPoints.length; i++) { + var a = accessPoints[i]; + d.innerHTML = d.innerHTML + "<p>" + a.mac + " " + a.ssid + " " + a.signal + "</p>"; + } + + var c = document.getElementById("c"); + c.innerHTML = "<p>" + count++ + "</p>"; + + }, + + onError: function (value) { + alert("error: " +value); + }, + + QueryInterface: function(iid) { + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + if (iid.equals(Components.interfaces.nsIWifiListener) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, +} + + + netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); + + var listener = new test(); + var wifi_service = Components.classes["@mozilla.org/wifi/monitor;1"].getService(Components.interfaces.nsIWifiMonitor); + + wifi_service.startWatching(listener); + + + +</script> +</head> + +<body> +<div id="d"><p></p></div> +<div id="c"><p></p></div> +</body> +</html> diff --git a/netwerk/wifi/win_wifiScanner.cpp b/netwerk/wifi/win_wifiScanner.cpp new file mode 100644 index 000000000..a462b9696 --- /dev/null +++ b/netwerk/wifi/win_wifiScanner.cpp @@ -0,0 +1,195 @@ +/* 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: + InterfaceScanCallbackData(uint32_t numInterfaces) + : mCurrentlyScanningInterfaces(numInterfaces) + { + mAllInterfacesDoneScanningEvent = + ::CreateEvent(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 +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 = 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 000000000..b43c23899 --- /dev/null +++ b/netwerk/wifi/win_wifiScanner.h @@ -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/. */ + +#pragma once + +// Moz headers (alphabetical) +#include "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "win_wlanLibrary.h" + +class nsWifiAccessPoint; + +// This class allows the wifi monitor to use WinWifiScanner and WinXPWifiScanner interchangeably. +class WindowsWifiScannerInterface { +public: + virtual ~WindowsWifiScannerInterface() {} + + virtual nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints) = 0; +}; + + +class WinWifiScanner : public WindowsWifiScannerInterface { + public: + WinWifiScanner(); + virtual ~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: + nsAutoPtr<WinWLANLibrary> mWlanLibrary; +}; diff --git a/netwerk/wifi/win_wlanLibrary.cpp b/netwerk/wifi/win_wlanLibrary.cpp new file mode 100644 index 000000000..cf1052788 --- /dev/null +++ b/netwerk/wifi/win_wlanLibrary.cpp @@ -0,0 +1,155 @@ +/* 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 = LoadLibrary("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 000000000..3daf4618e --- /dev/null +++ b/netwerk/wifi/win_wlanLibrary.h @@ -0,0 +1,61 @@ +/* -*- 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(WinWLANLibrary* library, void* object) + : mLibrary(library) + , mObject(object) + { + } + + ~ScopedWLANObject() + { + (*(mLibrary->GetWlanFreeMemoryPtr()))(mObject); + } + + private: + WinWLANLibrary *mLibrary; + void *mObject; +}; diff --git a/netwerk/wifi/win_xp_wifiScanner.cpp b/netwerk/wifi/win_xp_wifiScanner.cpp new file mode 100644 index 000000000..4dcd34034 --- /dev/null +++ b/netwerk/wifi/win_xp_wifiScanner.cpp @@ -0,0 +1,399 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Windows Vista uses the Native Wifi (WLAN) API for accessing WiFi cards. See +// http://msdn.microsoft.com/en-us/library/ms705945(VS.85).aspx. Windows XP +// Service Pack 3 (and Windows XP Service Pack 2, if upgraded with a hot fix) +// also support a limited version of the WLAN API. See +// http://msdn.microsoft.com/en-us/library/bb204766.aspx. The WLAN API uses +// wlanapi.h, which is not part of the SDK used by Gears, so is replicated +// locally using data from the MSDN. +//\ +// Windows XP from Service Pack 2 onwards supports the Wireless Zero +// Configuration (WZC) programming interface. See +// http://msdn.microsoft.com/en-us/library/ms706587(VS.85).aspx. +// +// The MSDN recommends that one use the WLAN API where available, and WZC +// otherwise. +// +// However, it seems that WZC fails for some wireless cards. Also, WLAN seems +// not to work on XP SP3. So we use WLAN on Vista, and use NDIS directly +// otherwise. + +// MOZILLA NOTE: +// This code is ported from chromium: +// https://chromium.googlesource.com/chromium/src/+/master/content/browser/geolocation/wifi_data_provider_win.cc +// Based on changeset 42c5878 + +#include "win_xp_wifiScanner.h" +#include "nsWifiAccessPoint.h" +#include <windows.h> +#include <winioctl.h> +#include <wlanapi.h> +#include <string> +#include <vector> + +// Taken from ndis.h for WinCE. +#define NDIS_STATUS_INVALID_LENGTH ((NDIS_STATUS)0xC0010014L) +#define NDIS_STATUS_BUFFER_TOO_SHORT ((NDIS_STATUS)0xC0010016L) + +namespace { +// The limits on the size of the buffer used for the OID query. +const int kInitialBufferSize = 2 << 12; // Good for about 50 APs. +const int kMaximumBufferSize = 2 << 20; // 2MB + +// Length for generic string buffers passed to Win32 APIs. +const int kStringLength = 512; + +// WlanOpenHandle +typedef DWORD (WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion, + PVOID pReserved, + PDWORD pdwNegotiatedVersion, + PHANDLE phClientHandle); + +// WlanEnumInterfaces +typedef DWORD (WINAPI* WlanEnumInterfacesFunction)( + HANDLE hClientHandle, + PVOID pReserved, + PWLAN_INTERFACE_INFO_LIST* ppInterfaceList); + +// WlanGetNetworkBssList +typedef DWORD (WINAPI* WlanGetNetworkBssListFunction)( + HANDLE hClientHandle, + const GUID* pInterfaceGuid, + const PDOT11_SSID pDot11Ssid, + DOT11_BSS_TYPE dot11BssType, + BOOL bSecurityEnabled, + PVOID pReserved, + PWLAN_BSS_LIST* ppWlanBssList +); + +// WlanFreeMemory +typedef VOID (WINAPI* WlanFreeMemoryFunction)(PVOID pMemory); + +// WlanCloseHandle +typedef DWORD (WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle, + PVOID pReserved); + +// Extracts data for an access point and converts to Gears format. +bool UndefineDosDevice(const std::string& device_name); +bool DefineDosDeviceIfNotExists(const std::string& device_name); +HANDLE GetFileHandle(const std::string& device_name); +// Makes the OID query and returns a Win32 error code. +int PerformQuery(HANDLE adapter_handle, std::vector<char>& buffer, DWORD* bytes_out); +bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer); +// Gets the system directory and appends a trailing slash if not already +// present. +bool GetSystemDirectory(std::string* path); + +bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data); +int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list, + int list_size, + nsCOMArray<nsWifiAccessPoint>& outData); +} // namespace + +class WindowsNdisApi +{ +public: + virtual ~WindowsNdisApi(); + static WindowsNdisApi* Create(); + virtual bool GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData); + +private: + static bool GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out); + // Swaps in content of the vector passed + explicit WindowsNdisApi(std::vector<std::string>* interface_service_names); + bool GetInterfaceDataNDIS(HANDLE adapter_handle, nsCOMArray<nsWifiAccessPoint>& outData); + // NDIS variables. + std::vector<std::string> interface_service_names_; + std::vector<char> _buffer; +}; + +// WindowsNdisApi +WindowsNdisApi::WindowsNdisApi( + std::vector<std::string>* interface_service_names) + : _buffer(kInitialBufferSize) { + interface_service_names_.swap(*interface_service_names); +} + +WindowsNdisApi::~WindowsNdisApi() { +} + +WindowsNdisApi* WindowsNdisApi::Create() { + std::vector<std::string> interface_service_names; + if (GetInterfacesNDIS(interface_service_names)) { + return new WindowsNdisApi(&interface_service_names); + } + return NULL; +} + +bool WindowsNdisApi::GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData) { + int interfaces_failed = 0; + int interfaces_succeeded = 0; + + for (int i = 0; i < static_cast<int>(interface_service_names_.size()); ++i) { + // First, check that we have a DOS device for this adapter. + if (!DefineDosDeviceIfNotExists(interface_service_names_[i])) { + continue; + } + + // Get the handle to the device. This will fail if the named device is not + // valid. + HANDLE adapter_handle = GetFileHandle(interface_service_names_[i]); + if (adapter_handle == INVALID_HANDLE_VALUE) { + continue; + } + + // Get the data. + if (GetInterfaceDataNDIS(adapter_handle, outData)) { + ++interfaces_succeeded; + } else { + ++interfaces_failed; + } + + // Clean up. + CloseHandle(adapter_handle); + UndefineDosDevice(interface_service_names_[i]); + } + + // Return true if at least one interface succeeded, or at the very least none + // failed. + return interfaces_succeeded > 0 || interfaces_failed == 0; +} + +bool WindowsNdisApi::GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out) { + HKEY network_cards_key = NULL; + if (RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", + 0, + KEY_READ, + &network_cards_key) != ERROR_SUCCESS) { + return false; + } + if (!network_cards_key) { + return false; + } + + for (int i = 0; ; ++i) { + TCHAR name[kStringLength]; + DWORD name_size = kStringLength; + FILETIME time; + if (RegEnumKeyEx(network_cards_key, + i, + name, + &name_size, + NULL, + NULL, + NULL, + &time) != ERROR_SUCCESS) { + break; + } + HKEY hardware_key = NULL; + if (RegOpenKeyEx(network_cards_key, name, 0, KEY_READ, &hardware_key) != + ERROR_SUCCESS) { + break; + } + if (!hardware_key) { + return false; + } + + TCHAR service_name[kStringLength]; + DWORD service_name_size = kStringLength; + DWORD type = 0; + if (RegQueryValueEx(hardware_key, + "ServiceName", + NULL, + &type, + reinterpret_cast<LPBYTE>(service_name), + &service_name_size) == ERROR_SUCCESS) { + interface_service_names_out.push_back(service_name); + } + RegCloseKey(hardware_key); + } + + RegCloseKey(network_cards_key); + return true; +} + +bool WindowsNdisApi::GetInterfaceDataNDIS(HANDLE adapter_handle, + nsCOMArray<nsWifiAccessPoint>& outData) { + DWORD bytes_out; + int result; + + while (true) { + bytes_out = 0; + result = PerformQuery(adapter_handle, _buffer, &bytes_out); + if (result == ERROR_GEN_FAILURE || // Returned by some Intel cards. + result == ERROR_INSUFFICIENT_BUFFER || + result == ERROR_MORE_DATA || + result == NDIS_STATUS_INVALID_LENGTH || + result == NDIS_STATUS_BUFFER_TOO_SHORT) { + // The buffer we supplied is too small, so increase it. bytes_out should + // provide the required buffer size, but this is not always the case. + size_t newSize; + if (bytes_out > static_cast<DWORD>(_buffer.size())) { + newSize = bytes_out; + } else { + newSize = _buffer.size() * 2; + } + if (!ResizeBuffer(newSize, _buffer)) { + return false; + } + } else { + // The buffer is not too small. + break; + } + } + + if (result == ERROR_SUCCESS) { + NDIS_802_11_BSSID_LIST* bssid_list = + reinterpret_cast<NDIS_802_11_BSSID_LIST*>(&_buffer[0]); + GetDataFromBssIdList(*bssid_list, _buffer.size(), outData); + } + + return true; +} + +namespace { +#define uint8 unsigned char + +bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data) +{ + access_point_data->setMac(data.MacAddress); + access_point_data->setSignal(data.Rssi); + // Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated. + const unsigned char* ssid = data.Ssid.Ssid; + size_t len = data.Ssid.SsidLength; + access_point_data->setSSID(reinterpret_cast<const char*>(ssid), len); + return true; +} + +int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list, + int list_size, + nsCOMArray<nsWifiAccessPoint>& outData) +{ + // Walk through the BSS IDs. + int found = 0; + const uint8* iterator = reinterpret_cast<const uint8*>(&bss_id_list.Bssid[0]); + const uint8* end_of_buffer = + reinterpret_cast<const uint8*>(&bss_id_list) + list_size; + for (int i = 0; i < static_cast<int>(bss_id_list.NumberOfItems); ++i) { + const NDIS_WLAN_BSSID *bss_id = + reinterpret_cast<const NDIS_WLAN_BSSID*>(iterator); + // Check that the length of this BSS ID is reasonable. + if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) || + iterator + bss_id->Length > end_of_buffer) { + break; + } + nsWifiAccessPoint* ap = new nsWifiAccessPoint(); + if (ConvertToAccessPointData(*bss_id, ap)) { + outData.AppendObject(ap); + ++found; + } + // Move to the next BSS ID. + iterator += bss_id->Length; + } + return found; +} + + +bool UndefineDosDevice(const std::string& device_name) { + // We remove only the mapping we use, that is \Device\<device_name>. + std::string target_path = "\\Device\\" + device_name; + return DefineDosDevice( + DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, + device_name.c_str(), + target_path.c_str()) == TRUE; +} + +bool DefineDosDeviceIfNotExists(const std::string& device_name) { + // We create a DOS device name for the device at \Device\<device_name>. + std::string target_path = "\\Device\\" + device_name; + + TCHAR target[kStringLength]; + if (QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 && + target_path.compare(target) == 0) { + // Device already exists. + return true; + } + + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + return false; + } + + if (!DefineDosDevice(DDD_RAW_TARGET_PATH, + device_name.c_str(), + target_path.c_str())) { + return false; + } + + // Check that the device is really there. + return QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 && + target_path.compare(target) == 0; +} + +HANDLE GetFileHandle(const std::string& device_name) { + // We access a device with DOS path \Device\<device_name> at + // \\.\<device_name>. + std::string formatted_device_name = "\\\\.\\" + device_name; + + return CreateFile(formatted_device_name.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode + 0, // security attributes + OPEN_EXISTING, + 0, // flags and attributes + INVALID_HANDLE_VALUE); +} + +int PerformQuery(HANDLE adapter_handle, + std::vector<char>& buffer, + DWORD* bytes_out) { + DWORD oid = OID_802_11_BSSID_LIST; + if (!DeviceIoControl(adapter_handle, + IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, + sizeof(oid), + &buffer[0], + buffer.size(), + bytes_out, + NULL)) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer) { + if (requested_size > kMaximumBufferSize) { + buffer.resize(kInitialBufferSize); + return false; + } + + buffer.resize(requested_size); + return true; +} + +} // namespace + + +nsresult +WinXPWifiScanner::GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints) +{ + if (!mImplementation) { + mImplementation = WindowsNdisApi::Create(); + if (!mImplementation) { + return NS_ERROR_FAILURE; + } + } + + accessPoints.Clear(); + bool isOk = mImplementation->GetAccessPointData(accessPoints); + if (!isOk) { + mImplementation = 0; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} diff --git a/netwerk/wifi/win_xp_wifiScanner.h b/netwerk/wifi/win_xp_wifiScanner.h new file mode 100644 index 000000000..33ae4cae4 --- /dev/null +++ b/netwerk/wifi/win_xp_wifiScanner.h @@ -0,0 +1,25 @@ +/* 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 WINXPWIFISCANNER_H_ +#define WINXPWIFISCANNER_H_ + +#include "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "win_wifiScanner.h" + +class nsWifiAccessPoint; +class WindowsNdisApi; + +// This class is wrapper into the Chromium WindowNdisApi class for scanning wifis +// on Windows XP. When Firefox drops XP support, this code can go. +class WinXPWifiScanner : public WindowsWifiScannerInterface { +public: + nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints); + virtual ~WinXPWifiScanner() {} +private: + nsAutoPtr<WindowsNdisApi> mImplementation; +}; + +#endif
\ No newline at end of file |