Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
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"
13 using namespace mozilla::dom;
15 #define BUFFER_SIZE 4096
16 #define COMMAND_SIZE 256
17 #define PROPERTY_VALUE_MAX 80
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;
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 }
32 static void*
33 GetSharedLibrary()
34 {
35 PR_CallOnce(&sInitWifiLib, InitWifiLib);
36 return sWifiLib;
37 }
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 }
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
51 static const uint32_t REPLACE_UTF8 = 0xFFFD;
53 void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
54 {
55 JS::UTF8Chars src(aInput, aLength);
57 char16_t dst[aLength]; // Allocating for worst case.
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++;
74 #define INVALID(report, arg, n2) \
75 do { \
76 n = n2; \
77 goto invalidMultiByteCodeUnit; \
78 } while (0)
80 // Check the leading byte.
81 if (n < 2 || n > 4)
82 INVALID(ReportInvalidCharacter, i, 1);
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);
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 }
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);
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 }
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 }
129 dst[j] = 0;
130 aOut = dst;
131 }
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 }
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 }
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, )
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 }
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 }
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 }
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 }
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 }
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 };
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 }
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 }
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 }
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 }
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 }
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 };
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 }
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 }
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 };
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 };
279 void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface)
280 {
281 CHECK_HWLIB()
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 }
288 #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
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 }
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 }
310 // Always correlate the opaque ids.
311 aResult.mId = aOptions.mId;
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);
374 if (aResult.mStatus == -1) {
375 // Early return since we failed.
376 return true;
377 }
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);
388 uint32_t inet4; // only support IPv4 for now.
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
396 INET_PTON(ipaddr, mIpaddr);
397 INET_PTON(gateway, mGateway);
399 if (dns1[0] != '\0') {
400 INET_PTON(dns1, mDns1);
401 }
403 if (dns2[0] != '\0') {
404 INET_PTON(dns2, mDns2);
405 }
407 INET_PTON(server, mServer);
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 }
421 return true;
422 }
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 }