|
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 "WifiUtils.h" |
|
6 #include <dlfcn.h> |
|
7 #include <errno.h> |
|
8 #include <cutils/properties.h> |
|
9 #include "prinit.h" |
|
10 #include "js/CharacterEncoding.h" |
|
11 #include "mozilla/dom/network/NetUtils.h" |
|
12 |
|
13 using namespace mozilla::dom; |
|
14 |
|
15 #define BUFFER_SIZE 4096 |
|
16 #define COMMAND_SIZE 256 |
|
17 #define PROPERTY_VALUE_MAX 80 |
|
18 |
|
19 // Intentionally not trying to dlclose() this handle. That's playing |
|
20 // Russian roulette with security bugs. |
|
21 static void* sWifiLib; |
|
22 static PRCallOnceType sInitWifiLib; |
|
23 |
|
24 static PRStatus |
|
25 InitWifiLib() |
|
26 { |
|
27 sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY); |
|
28 // We might fail to open the hardware lib. That's OK. |
|
29 return PR_SUCCESS; |
|
30 } |
|
31 |
|
32 static void* |
|
33 GetSharedLibrary() |
|
34 { |
|
35 PR_CallOnce(&sInitWifiLib, InitWifiLib); |
|
36 return sWifiLib; |
|
37 } |
|
38 |
|
39 static bool |
|
40 GetWifiP2pSupported() |
|
41 { |
|
42 char propP2pSupported[PROPERTY_VALUE_MAX]; |
|
43 property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0"); |
|
44 return (0 == strcmp(propP2pSupported, "1")); |
|
45 } |
|
46 |
|
47 // This is the same algorithm as in InflateUTF8StringToBuffer with Copy and |
|
48 // while ignoring invalids. |
|
49 // https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231 |
|
50 |
|
51 static const uint32_t REPLACE_UTF8 = 0xFFFD; |
|
52 |
|
53 void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut) |
|
54 { |
|
55 JS::UTF8Chars src(aInput, aLength); |
|
56 |
|
57 char16_t dst[aLength]; // Allocating for worst case. |
|
58 |
|
59 // First, count how many jschars need to be in the inflated string. |
|
60 // |i| is the index into |src|, and |j| is the the index into |dst|. |
|
61 size_t srclen = src.length(); |
|
62 uint32_t j = 0; |
|
63 for (uint32_t i = 0; i < srclen; i++, j++) { |
|
64 uint32_t v = uint32_t(src[i]); |
|
65 if (!(v & 0x80)) { |
|
66 // ASCII code unit. Simple copy. |
|
67 dst[j] = char16_t(v); |
|
68 } else { |
|
69 // Non-ASCII code unit. Determine its length in bytes (n). |
|
70 uint32_t n = 1; |
|
71 while (v & (0x80 >> n)) |
|
72 n++; |
|
73 |
|
74 #define INVALID(report, arg, n2) \ |
|
75 do { \ |
|
76 n = n2; \ |
|
77 goto invalidMultiByteCodeUnit; \ |
|
78 } while (0) |
|
79 |
|
80 // Check the leading byte. |
|
81 if (n < 2 || n > 4) |
|
82 INVALID(ReportInvalidCharacter, i, 1); |
|
83 |
|
84 // Check that |src| is large enough to hold an n-byte code unit. |
|
85 if (i + n > srclen) |
|
86 INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1); |
|
87 |
|
88 // Check the second byte. From Unicode Standard v6.2, Table 3-7 |
|
89 // Well-Formed UTF-8 Byte Sequences. |
|
90 if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) || // E0 A0~BF |
|
91 (v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) || // ED 80~9F |
|
92 (v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) || // F0 90~BF |
|
93 (v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80)) // F4 80~8F |
|
94 { |
|
95 INVALID(ReportInvalidCharacter, i, 1); |
|
96 } |
|
97 |
|
98 // Check the continuation bytes. |
|
99 for (uint32_t m = 1; m < n; m++) |
|
100 if ((src[i + m] & 0xC0) != 0x80) |
|
101 INVALID(ReportInvalidCharacter, i, m); |
|
102 |
|
103 // Determine the code unit's length in jschars and act accordingly. |
|
104 v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n); |
|
105 if (v < 0x10000) { |
|
106 // The n-byte UTF8 code unit will fit in a single jschar. |
|
107 dst[j] = jschar(v); |
|
108 } else { |
|
109 v -= 0x10000; |
|
110 if (v <= 0xFFFFF) { |
|
111 // The n-byte UTF8 code unit will fit in two jschars. |
|
112 dst[j] = jschar((v >> 10) + 0xD800); |
|
113 j++; |
|
114 dst[j] = jschar((v & 0x3FF) + 0xDC00); |
|
115 } else { |
|
116 // The n-byte UTF8 code unit won't fit in two jschars. |
|
117 INVALID(ReportTooBigCharacter, v, 1); |
|
118 } |
|
119 } |
|
120 |
|
121 invalidMultiByteCodeUnit: |
|
122 // Move i to the last byte of the multi-byte code unit; the loop |
|
123 // header will do the final i++ to move to the start of the next |
|
124 // code unit. |
|
125 i += n - 1; |
|
126 } |
|
127 } |
|
128 |
|
129 dst[j] = 0; |
|
130 aOut = dst; |
|
131 } |
|
132 |
|
133 // Helper to check we have loaded the hardware shared library. |
|
134 #define CHECK_HWLIB(ret) \ |
|
135 void* hwLib = GetSharedLibrary(); \ |
|
136 if (!hwLib) { \ |
|
137 NS_WARNING("No /system/lib/libhardware_legacy.so"); \ |
|
138 return ret; \ |
|
139 } |
|
140 |
|
141 #define DEFAULT_IMPL(name, ret, args...) \ |
|
142 DEFINE_DLFUNC(name, ret, args...) \ |
|
143 ret do_##name(args) { \ |
|
144 USE_DLFUNC(name) \ |
|
145 return name(args); \ |
|
146 } |
|
147 |
|
148 // ICS implementation. |
|
149 class ICSWpaSupplicantImpl : public WpaSupplicantImpl |
|
150 { |
|
151 public: |
|
152 DEFAULT_IMPL(wifi_load_driver, int32_t, ) |
|
153 DEFAULT_IMPL(wifi_unload_driver, int32_t, ) |
|
154 |
|
155 DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t) |
|
156 int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) { |
|
157 USE_DLFUNC(wifi_wait_for_event) |
|
158 return wifi_wait_for_event(buf, len); |
|
159 } |
|
160 |
|
161 DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) |
|
162 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { |
|
163 USE_DLFUNC(wifi_command) |
|
164 return wifi_command(cmd, buf, len); |
|
165 } |
|
166 |
|
167 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, ) |
|
168 int32_t do_wifi_start_supplicant(int32_t) { |
|
169 USE_DLFUNC(wifi_start_supplicant) |
|
170 return wifi_start_supplicant(); |
|
171 } |
|
172 |
|
173 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t) |
|
174 int32_t do_wifi_stop_supplicant(int32_t) { |
|
175 USE_DLFUNC(wifi_stop_supplicant) |
|
176 return wifi_stop_supplicant(); |
|
177 } |
|
178 |
|
179 DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, ) |
|
180 int32_t do_wifi_connect_to_supplicant(const char* iface) { |
|
181 USE_DLFUNC(wifi_connect_to_supplicant) |
|
182 return wifi_connect_to_supplicant(); |
|
183 } |
|
184 |
|
185 DEFINE_DLFUNC(wifi_close_supplicant_connection, void, ) |
|
186 void do_wifi_close_supplicant_connection(const char* iface) { |
|
187 USE_DLFUNC(wifi_close_supplicant_connection) |
|
188 return wifi_close_supplicant_connection(); |
|
189 } |
|
190 }; |
|
191 |
|
192 // JB implementation. |
|
193 // We only redefine the methods that have a different signature than on ICS. |
|
194 class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl |
|
195 { |
|
196 public: |
|
197 DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t) |
|
198 int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) { |
|
199 USE_DLFUNC(wifi_wait_for_event) |
|
200 return wifi_wait_for_event(iface, buf, len); |
|
201 } |
|
202 |
|
203 DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*) |
|
204 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { |
|
205 USE_DLFUNC(wifi_command) |
|
206 return wifi_command(iface, cmd, buf, len); |
|
207 } |
|
208 |
|
209 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) |
|
210 int32_t do_wifi_start_supplicant(int32_t arg) { |
|
211 USE_DLFUNC(wifi_start_supplicant) |
|
212 return wifi_start_supplicant(arg); |
|
213 } |
|
214 |
|
215 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) |
|
216 int32_t do_wifi_stop_supplicant(int32_t arg) { |
|
217 USE_DLFUNC(wifi_stop_supplicant) |
|
218 return wifi_stop_supplicant(arg); |
|
219 } |
|
220 |
|
221 DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*) |
|
222 int32_t do_wifi_connect_to_supplicant(const char* iface) { |
|
223 USE_DLFUNC(wifi_connect_to_supplicant) |
|
224 return wifi_connect_to_supplicant(iface); |
|
225 } |
|
226 |
|
227 DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*) |
|
228 void do_wifi_close_supplicant_connection(const char* iface) { |
|
229 USE_DLFUNC(wifi_close_supplicant_connection) |
|
230 wifi_close_supplicant_connection(iface); |
|
231 } |
|
232 }; |
|
233 |
|
234 // KK implementation. |
|
235 // We only redefine the methods that have a different signature than on ICS. |
|
236 class KKWpaSupplicantImpl : public ICSWpaSupplicantImpl |
|
237 { |
|
238 public: |
|
239 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) |
|
240 int32_t do_wifi_start_supplicant(int32_t arg) { |
|
241 USE_DLFUNC(wifi_start_supplicant) |
|
242 return wifi_start_supplicant(arg); |
|
243 } |
|
244 |
|
245 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) |
|
246 int32_t do_wifi_stop_supplicant(int32_t arg) { |
|
247 USE_DLFUNC(wifi_stop_supplicant) |
|
248 return wifi_stop_supplicant(arg); |
|
249 } |
|
250 |
|
251 DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) |
|
252 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { |
|
253 char command[COMMAND_SIZE]; |
|
254 if (!strcmp(iface, "p2p0")) { |
|
255 // Commands for p2p0 interface don't need prefix |
|
256 snprintf(command, COMMAND_SIZE, "%s", cmd); |
|
257 } |
|
258 else { |
|
259 snprintf(command, COMMAND_SIZE, "IFNAME=%s %s", iface, cmd); |
|
260 } |
|
261 USE_DLFUNC(wifi_command) |
|
262 return wifi_command(command, buf, len); |
|
263 } |
|
264 }; |
|
265 |
|
266 // Concrete class to use to access the wpa supplicant. |
|
267 WpaSupplicant::WpaSupplicant() |
|
268 { |
|
269 if (NetUtils::SdkVersion() < 16) { |
|
270 mImpl = new ICSWpaSupplicantImpl(); |
|
271 } else if (NetUtils::SdkVersion() < 19) { |
|
272 mImpl = new JBWpaSupplicantImpl(); |
|
273 } else { |
|
274 mImpl = new KKWpaSupplicantImpl(); |
|
275 } |
|
276 mNetUtils = new NetUtils(); |
|
277 }; |
|
278 |
|
279 void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface) |
|
280 { |
|
281 CHECK_HWLIB() |
|
282 |
|
283 char buffer[BUFFER_SIZE]; |
|
284 int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE); |
|
285 CheckBuffer(buffer, ret, aEvent); |
|
286 } |
|
287 |
|
288 #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get() |
|
289 |
|
290 /** |
|
291 * Make a subnet mask. |
|
292 */ |
|
293 uint32_t WpaSupplicant::MakeMask(uint32_t len) { |
|
294 uint32_t mask = 0; |
|
295 for (uint32_t i = 0; i < len; ++i) { |
|
296 mask |= (0x80000000 >> i); |
|
297 } |
|
298 return ntohl(mask); |
|
299 } |
|
300 |
|
301 bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions, |
|
302 WifiResultOptions& aResult, |
|
303 const nsCString& aInterface) |
|
304 { |
|
305 CHECK_HWLIB(false) |
|
306 if (!mNetUtils->GetSharedLibrary()) { |
|
307 return false; |
|
308 } |
|
309 |
|
310 // Always correlate the opaque ids. |
|
311 aResult.mId = aOptions.mId; |
|
312 |
|
313 if (aOptions.mCmd.EqualsLiteral("command")) { |
|
314 size_t len = BUFFER_SIZE - 1; |
|
315 char buffer[BUFFER_SIZE]; |
|
316 NS_ConvertUTF16toUTF8 request(aOptions.mRequest); |
|
317 aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len); |
|
318 nsString value; |
|
319 if (aResult.mStatus == 0) { |
|
320 if (buffer[len - 1] == '\n') { // remove trailing new lines. |
|
321 len--; |
|
322 } |
|
323 buffer[len] = '\0'; |
|
324 CheckBuffer(buffer, len, value); |
|
325 } |
|
326 aResult.mReply = value; |
|
327 } else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) { |
|
328 mImpl->do_wifi_close_supplicant_connection(aInterface.get()); |
|
329 } else if (aOptions.mCmd.EqualsLiteral("load_driver")) { |
|
330 aResult.mStatus = mImpl->do_wifi_load_driver(); |
|
331 } else if (aOptions.mCmd.EqualsLiteral("unload_driver")) { |
|
332 aResult.mStatus = mImpl->do_wifi_unload_driver(); |
|
333 } else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) { |
|
334 aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0); |
|
335 } else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) { |
|
336 aResult.mStatus = mImpl->do_wifi_stop_supplicant(0); |
|
337 } else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) { |
|
338 aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get()); |
|
339 } else if (aOptions.mCmd.EqualsLiteral("ifc_enable")) { |
|
340 aResult.mStatus = mNetUtils->do_ifc_enable(GET_CHAR(mIfname)); |
|
341 } else if (aOptions.mCmd.EqualsLiteral("ifc_disable")) { |
|
342 aResult.mStatus = mNetUtils->do_ifc_disable(GET_CHAR(mIfname)); |
|
343 } else if (aOptions.mCmd.EqualsLiteral("ifc_configure")) { |
|
344 aResult.mStatus = mNetUtils->do_ifc_configure( |
|
345 GET_CHAR(mIfname), aOptions.mIpaddr, aOptions.mMask, |
|
346 aOptions.mGateway, aOptions.mDns1, aOptions.mDns2 |
|
347 ); |
|
348 } else if (aOptions.mCmd.EqualsLiteral("ifc_reset_connections")) { |
|
349 aResult.mStatus = mNetUtils->do_ifc_reset_connections( |
|
350 GET_CHAR(mIfname), RESET_ALL_ADDRESSES |
|
351 ); |
|
352 } else if (aOptions.mCmd.EqualsLiteral("dhcp_stop")) { |
|
353 aResult.mStatus = mNetUtils->do_dhcp_stop(GET_CHAR(mIfname)); |
|
354 } else if (aOptions.mCmd.EqualsLiteral("dhcp_do_request")) { |
|
355 char ipaddr[PROPERTY_VALUE_MAX]; |
|
356 char gateway[PROPERTY_VALUE_MAX]; |
|
357 uint32_t prefixLength; |
|
358 char dns1[PROPERTY_VALUE_MAX]; |
|
359 char dns2[PROPERTY_VALUE_MAX]; |
|
360 char server[PROPERTY_VALUE_MAX]; |
|
361 uint32_t lease; |
|
362 char vendorinfo[PROPERTY_VALUE_MAX]; |
|
363 aResult.mStatus = |
|
364 mNetUtils->do_dhcp_do_request(GET_CHAR(mIfname), |
|
365 ipaddr, |
|
366 gateway, |
|
367 &prefixLength, |
|
368 dns1, |
|
369 dns2, |
|
370 server, |
|
371 &lease, |
|
372 vendorinfo); |
|
373 |
|
374 if (aResult.mStatus == -1) { |
|
375 // Early return since we failed. |
|
376 return true; |
|
377 } |
|
378 |
|
379 aResult.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr); |
|
380 aResult.mGateway_str = NS_ConvertUTF8toUTF16(gateway); |
|
381 aResult.mDns1_str = NS_ConvertUTF8toUTF16(dns1); |
|
382 aResult.mDns2_str = NS_ConvertUTF8toUTF16(dns2); |
|
383 aResult.mServer_str = NS_ConvertUTF8toUTF16(server); |
|
384 aResult.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo); |
|
385 aResult.mLease = lease; |
|
386 aResult.mMask = MakeMask(prefixLength); |
|
387 |
|
388 uint32_t inet4; // only support IPv4 for now. |
|
389 |
|
390 #define INET_PTON(var, field) \ |
|
391 PR_BEGIN_MACRO \ |
|
392 inet_pton(AF_INET, var, &inet4); \ |
|
393 aResult.field = inet4; \ |
|
394 PR_END_MACRO |
|
395 |
|
396 INET_PTON(ipaddr, mIpaddr); |
|
397 INET_PTON(gateway, mGateway); |
|
398 |
|
399 if (dns1[0] != '\0') { |
|
400 INET_PTON(dns1, mDns1); |
|
401 } |
|
402 |
|
403 if (dns2[0] != '\0') { |
|
404 INET_PTON(dns2, mDns2); |
|
405 } |
|
406 |
|
407 INET_PTON(server, mServer); |
|
408 |
|
409 //aResult.mask_str = netHelpers.ipToString(obj.mask); |
|
410 char inet_str[64]; |
|
411 if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) { |
|
412 aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str); |
|
413 } |
|
414 } else { |
|
415 NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command"); |
|
416 printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s", |
|
417 NS_ConvertUTF16toUTF8(aOptions.mCmd).get()); |
|
418 return false; |
|
419 } |
|
420 |
|
421 return true; |
|
422 } |
|
423 |
|
424 // Checks the buffer and do the utf processing. |
|
425 void |
|
426 WpaSupplicant::CheckBuffer(char* buffer, int32_t length, |
|
427 nsAString& aEvent) |
|
428 { |
|
429 if (length > 0 && length < BUFFER_SIZE) { |
|
430 buffer[length] = 0; |
|
431 LossyConvertUTF8toUTF16(buffer, length, aEvent); |
|
432 } |
|
433 } |