1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "nsWifiScannerDBus.h"
6 #include "mozilla/ipc/DBusMessageRefPtr.h"
7 #include "nsWifiAccessPoint.h"
8 
9 namespace mozilla {
10 
nsWifiScannerDBus(nsCOMArray<nsWifiAccessPoint> * aAccessPoints)11 nsWifiScannerDBus::nsWifiScannerDBus(nsCOMArray<nsWifiAccessPoint> *aAccessPoints)
12 : mAccessPoints(aAccessPoints)
13 {
14   MOZ_ASSERT(mAccessPoints);
15 
16   mConnection =
17     already_AddRefed<DBusConnection>(dbus_bus_get(DBUS_BUS_SYSTEM, nullptr));
18   MOZ_ASSERT(mConnection);
19   dbus_connection_set_exit_on_disconnect(mConnection, false);
20 
21   MOZ_COUNT_CTOR(nsWifiScannerDBus);
22 }
23 
~nsWifiScannerDBus()24 nsWifiScannerDBus::~nsWifiScannerDBus()
25 {
26   MOZ_COUNT_DTOR(nsWifiScannerDBus);
27 }
28 
29 nsresult
Scan()30 nsWifiScannerDBus::Scan()
31 {
32   return SendMessage("org.freedesktop.NetworkManager",
33                      "/org/freedesktop/NetworkManager",
34                      "GetDevices");
35 }
36 
37 nsresult
SendMessage(const char * aInterface,const char * aPath,const char * aFuncCall)38 nsWifiScannerDBus::SendMessage(const char* aInterface,
39                                const char* aPath,
40                                const char* aFuncCall)
41 {
42   RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
43     dbus_message_new_method_call("org.freedesktop.NetworkManager",
44                                  aPath, aInterface, aFuncCall));
45   if (!msg) {
46     return NS_ERROR_FAILURE;
47   }
48 
49   DBusMessageIter argsIter;
50   dbus_message_iter_init_append(msg, &argsIter);
51 
52   if (!strcmp(aFuncCall, "Get")) {
53     const char* paramInterface = "org.freedesktop.NetworkManager.Device";
54     if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING,
55                                         &paramInterface)) {
56       return NS_ERROR_FAILURE;
57     }
58 
59     const char* paramDeviceType = "DeviceType";
60     if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING,
61                                         &paramDeviceType)) {
62       return NS_ERROR_FAILURE;
63     }
64   } else if (!strcmp(aFuncCall, "GetAll")) {
65     const char* param = "";
66     if (!dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, &param)) {
67       return NS_ERROR_FAILURE;
68     }
69   }
70 
71   DBusError err;
72   dbus_error_init(&err);
73 
74   // http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html
75   // Refer to function dbus_connection_send_with_reply_and_block.
76   const uint32_t DBUS_DEFAULT_TIMEOUT = -1;
77   RefPtr<DBusMessage> reply = already_AddRefed<DBusMessage>(
78     dbus_connection_send_with_reply_and_block(mConnection, msg,
79                                               DBUS_DEFAULT_TIMEOUT, &err));
80   if (dbus_error_is_set(&err)) {
81     dbus_error_free(&err);
82 
83     // In the GetAccessPoints case, if there are no access points, error is set.
84     // We don't want to error out here.
85     if (!strcmp(aFuncCall, "GetAccessPoints")) {
86       return NS_OK;
87     }
88     return NS_ERROR_FAILURE;
89   }
90 
91   nsresult rv;
92   if (!strcmp(aFuncCall, "GetDevices")) {
93     rv = IdentifyDevices(reply);
94   } else if (!strcmp(aFuncCall, "Get")) {
95     rv = IdentifyDeviceType(reply, aPath);
96   } else if (!strcmp(aFuncCall, "GetAccessPoints")) {
97     rv = IdentifyAccessPoints(reply);
98   } else if (!strcmp(aFuncCall, "GetAll")) {
99     rv = IdentifyAPProperties(reply);
100   } else {
101     rv = NS_ERROR_FAILURE;
102   }
103   return rv;
104 }
105 
106 nsresult
IdentifyDevices(DBusMessage * aMsg)107 nsWifiScannerDBus::IdentifyDevices(DBusMessage* aMsg)
108 {
109   DBusMessageIter iter;
110   nsresult rv = GetDBusIterator(aMsg, &iter);
111   NS_ENSURE_SUCCESS(rv, rv);
112 
113   const char* devicePath;
114   do {
115     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
116       return NS_ERROR_FAILURE;
117     }
118 
119     dbus_message_iter_get_basic(&iter, &devicePath);
120     if (!devicePath) {
121       return NS_ERROR_FAILURE;
122     }
123 
124     rv = SendMessage("org.freedesktop.DBus.Properties", devicePath, "Get");
125     NS_ENSURE_SUCCESS(rv, rv);
126   } while (dbus_message_iter_next(&iter));
127 
128   return NS_OK;
129 }
130 
131 nsresult
IdentifyDeviceType(DBusMessage * aMsg,const char * aDevicePath)132 nsWifiScannerDBus::IdentifyDeviceType(DBusMessage* aMsg, const char* aDevicePath)
133 {
134   DBusMessageIter args;
135   if (!dbus_message_iter_init(aMsg, &args)) {
136     return NS_ERROR_FAILURE;
137   }
138 
139   if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) {
140     return NS_ERROR_FAILURE;
141   }
142 
143   DBusMessageIter variantIter;
144   dbus_message_iter_recurse(&args, &variantIter);
145   if (dbus_message_iter_get_arg_type(&variantIter) != DBUS_TYPE_UINT32) {
146     return NS_ERROR_FAILURE;
147   }
148 
149   uint32_t deviceType;
150   dbus_message_iter_get_basic(&variantIter, &deviceType);
151 
152   // http://projects.gnome.org/NetworkManager/developers/api/07/spec-07.html
153   // Refer to NM_DEVICE_TYPE_WIFI under NM_DEVICE_TYPE.
154   const uint32_t NM_DEVICE_TYPE_WIFI = 2;
155   nsresult rv = NS_OK;
156   if (deviceType == NM_DEVICE_TYPE_WIFI) {
157     rv = SendMessage("org.freedesktop.NetworkManager.Device.Wireless",
158                      aDevicePath, "GetAccessPoints");
159   }
160 
161   return rv;
162 }
163 
164 nsresult
IdentifyAccessPoints(DBusMessage * aMsg)165 nsWifiScannerDBus::IdentifyAccessPoints(DBusMessage* aMsg)
166 {
167   DBusMessageIter iter;
168   nsresult rv = GetDBusIterator(aMsg, &iter);
169   NS_ENSURE_SUCCESS(rv, rv);
170 
171   const char* path;
172   do {
173     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
174       return NS_ERROR_FAILURE;
175     }
176     dbus_message_iter_get_basic(&iter, &path);
177     if (!path) {
178       return NS_ERROR_FAILURE;
179     }
180 
181     rv = SendMessage("org.freedesktop.DBus.Properties", path, "GetAll");
182     NS_ENSURE_SUCCESS(rv, rv);
183   } while (dbus_message_iter_next(&iter));
184 
185   return NS_OK;
186 }
187 
188 nsresult
IdentifyAPProperties(DBusMessage * aMsg)189 nsWifiScannerDBus::IdentifyAPProperties(DBusMessage* aMsg)
190 {
191   DBusMessageIter arr;
192   nsresult rv = GetDBusIterator(aMsg, &arr);
193   NS_ENSURE_SUCCESS(rv, rv);
194 
195   RefPtr<nsWifiAccessPoint> ap = new nsWifiAccessPoint();
196   do {
197     DBusMessageIter dict;
198     dbus_message_iter_recurse(&arr, &dict);
199 
200     do {
201       const char* key;
202       dbus_message_iter_get_basic(&dict, &key);
203       if (!key) {
204         return NS_ERROR_FAILURE;
205       }
206       dbus_message_iter_next(&dict);
207 
208       DBusMessageIter variant;
209       dbus_message_iter_recurse(&dict, &variant);
210 
211       if (!strncmp(key, "Ssid", strlen("Ssid"))) {
212         nsresult rv = StoreSsid(&variant, ap);
213         NS_ENSURE_SUCCESS(rv, rv);
214         break;
215       }
216 
217       if (!strncmp(key, "HwAddress", strlen("HwAddress"))) {
218         nsresult rv = SetMac(&variant, ap);
219         NS_ENSURE_SUCCESS(rv, rv);
220         break;
221       }
222 
223       if (!strncmp(key, "Strength", strlen("Strength"))) {
224         if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_BYTE) {
225           return NS_ERROR_FAILURE;
226         }
227 
228         uint8_t strength;
229         dbus_message_iter_get_basic(&variant, &strength);
230         ap->setSignal(strength);
231       }
232     } while (dbus_message_iter_next(&dict));
233   } while (dbus_message_iter_next(&arr));
234 
235   mAccessPoints->AppendObject(ap);
236   return NS_OK;
237 }
238 
239 nsresult
StoreSsid(DBusMessageIter * aVariant,nsWifiAccessPoint * aAp)240 nsWifiScannerDBus::StoreSsid(DBusMessageIter* aVariant, nsWifiAccessPoint* aAp)
241 {
242   if (dbus_message_iter_get_arg_type(aVariant) != DBUS_TYPE_ARRAY) {
243     return NS_ERROR_FAILURE;
244   }
245 
246   DBusMessageIter variantMember;
247   dbus_message_iter_recurse(aVariant, &variantMember);
248 
249   const uint32_t MAX_SSID_LEN = 32;
250   char ssid[MAX_SSID_LEN];
251   memset(ssid, '\0', ArrayLength(ssid));
252   uint32_t i = 0;
253   do {
254     if (dbus_message_iter_get_arg_type(&variantMember) != DBUS_TYPE_BYTE) {
255       return NS_ERROR_FAILURE;
256     }
257 
258     dbus_message_iter_get_basic(&variantMember, &ssid[i]);
259     i++;
260   } while (dbus_message_iter_next(&variantMember) && i < MAX_SSID_LEN);
261 
262   aAp->setSSID(ssid, i);
263   return NS_OK;
264 }
265 
266 nsresult
SetMac(DBusMessageIter * aVariant,nsWifiAccessPoint * aAp)267 nsWifiScannerDBus::SetMac(DBusMessageIter* aVariant, nsWifiAccessPoint* aAp)
268 {
269   if (dbus_message_iter_get_arg_type(aVariant) != DBUS_TYPE_STRING) {
270     return NS_ERROR_FAILURE;
271   }
272 
273   // hwAddress format is XX:XX:XX:XX:XX:XX. Need to convert to XXXXXX format.
274   char* hwAddress;
275   dbus_message_iter_get_basic(aVariant, &hwAddress);
276   if (!hwAddress) {
277     return NS_ERROR_FAILURE;
278   }
279 
280   const uint32_t MAC_LEN = 6;
281   uint8_t macAddress[MAC_LEN];
282   char* token = strtok(hwAddress, ":");
283   for (uint32_t i = 0; i < ArrayLength(macAddress); i++) {
284     if (!token) {
285       return NS_ERROR_FAILURE;
286     }
287     macAddress[i] = strtoul(token, nullptr, 16);
288     token = strtok(nullptr, ":");
289   }
290   aAp->setMac(macAddress);
291   return NS_OK;
292 }
293 
294 nsresult
GetDBusIterator(DBusMessage * aMsg,DBusMessageIter * aIterArray)295 nsWifiScannerDBus::GetDBusIterator(DBusMessage* aMsg,
296                                    DBusMessageIter* aIterArray)
297 {
298   DBusMessageIter iter;
299   if (!dbus_message_iter_init(aMsg, &iter)) {
300     return NS_ERROR_FAILURE;
301   }
302 
303   if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
304     return NS_ERROR_FAILURE;
305   }
306 
307   dbus_message_iter_recurse(&iter, aIterArray);
308   return NS_OK;
309 }
310 
311 } // mozilla
312 
313 nsresult
DoScan()314 nsWifiMonitor::DoScan()
315 {
316   nsCOMArray<nsWifiAccessPoint> accessPoints;
317   mozilla::nsWifiScannerDBus wifiScanner(&accessPoints);
318   nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
319 
320   while (mKeepGoing) {
321     accessPoints.Clear();
322     nsresult rv = wifiScanner.Scan();
323     NS_ENSURE_SUCCESS(rv, rv);
324     bool accessPointsChanged = !AccessPointsEqual(accessPoints,
325                                                   lastAccessPoints);
326     ReplaceArray(lastAccessPoints, accessPoints);
327 
328     rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
329     NS_ENSURE_SUCCESS(rv, rv);
330 
331     LOG(("waiting on monitor\n"));
332     mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
333     mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
334   }
335 
336   return NS_OK;
337 }
338