1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/wifi/WifiUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,433 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "WifiUtils.h" 1.9 +#include <dlfcn.h> 1.10 +#include <errno.h> 1.11 +#include <cutils/properties.h> 1.12 +#include "prinit.h" 1.13 +#include "js/CharacterEncoding.h" 1.14 +#include "mozilla/dom/network/NetUtils.h" 1.15 + 1.16 +using namespace mozilla::dom; 1.17 + 1.18 +#define BUFFER_SIZE 4096 1.19 +#define COMMAND_SIZE 256 1.20 +#define PROPERTY_VALUE_MAX 80 1.21 + 1.22 +// Intentionally not trying to dlclose() this handle. That's playing 1.23 +// Russian roulette with security bugs. 1.24 +static void* sWifiLib; 1.25 +static PRCallOnceType sInitWifiLib; 1.26 + 1.27 +static PRStatus 1.28 +InitWifiLib() 1.29 +{ 1.30 + sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY); 1.31 + // We might fail to open the hardware lib. That's OK. 1.32 + return PR_SUCCESS; 1.33 +} 1.34 + 1.35 +static void* 1.36 +GetSharedLibrary() 1.37 +{ 1.38 + PR_CallOnce(&sInitWifiLib, InitWifiLib); 1.39 + return sWifiLib; 1.40 +} 1.41 + 1.42 +static bool 1.43 +GetWifiP2pSupported() 1.44 +{ 1.45 + char propP2pSupported[PROPERTY_VALUE_MAX]; 1.46 + property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0"); 1.47 + return (0 == strcmp(propP2pSupported, "1")); 1.48 +} 1.49 + 1.50 +// This is the same algorithm as in InflateUTF8StringToBuffer with Copy and 1.51 +// while ignoring invalids. 1.52 +// https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231 1.53 + 1.54 +static const uint32_t REPLACE_UTF8 = 0xFFFD; 1.55 + 1.56 +void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut) 1.57 +{ 1.58 + JS::UTF8Chars src(aInput, aLength); 1.59 + 1.60 + char16_t dst[aLength]; // Allocating for worst case. 1.61 + 1.62 + // First, count how many jschars need to be in the inflated string. 1.63 + // |i| is the index into |src|, and |j| is the the index into |dst|. 1.64 + size_t srclen = src.length(); 1.65 + uint32_t j = 0; 1.66 + for (uint32_t i = 0; i < srclen; i++, j++) { 1.67 + uint32_t v = uint32_t(src[i]); 1.68 + if (!(v & 0x80)) { 1.69 + // ASCII code unit. Simple copy. 1.70 + dst[j] = char16_t(v); 1.71 + } else { 1.72 + // Non-ASCII code unit. Determine its length in bytes (n). 1.73 + uint32_t n = 1; 1.74 + while (v & (0x80 >> n)) 1.75 + n++; 1.76 + 1.77 + #define INVALID(report, arg, n2) \ 1.78 + do { \ 1.79 + n = n2; \ 1.80 + goto invalidMultiByteCodeUnit; \ 1.81 + } while (0) 1.82 + 1.83 + // Check the leading byte. 1.84 + if (n < 2 || n > 4) 1.85 + INVALID(ReportInvalidCharacter, i, 1); 1.86 + 1.87 + // Check that |src| is large enough to hold an n-byte code unit. 1.88 + if (i + n > srclen) 1.89 + INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1); 1.90 + 1.91 + // Check the second byte. From Unicode Standard v6.2, Table 3-7 1.92 + // Well-Formed UTF-8 Byte Sequences. 1.93 + if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) || // E0 A0~BF 1.94 + (v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) || // ED 80~9F 1.95 + (v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) || // F0 90~BF 1.96 + (v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80)) // F4 80~8F 1.97 + { 1.98 + INVALID(ReportInvalidCharacter, i, 1); 1.99 + } 1.100 + 1.101 + // Check the continuation bytes. 1.102 + for (uint32_t m = 1; m < n; m++) 1.103 + if ((src[i + m] & 0xC0) != 0x80) 1.104 + INVALID(ReportInvalidCharacter, i, m); 1.105 + 1.106 + // Determine the code unit's length in jschars and act accordingly. 1.107 + v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n); 1.108 + if (v < 0x10000) { 1.109 + // The n-byte UTF8 code unit will fit in a single jschar. 1.110 + dst[j] = jschar(v); 1.111 + } else { 1.112 + v -= 0x10000; 1.113 + if (v <= 0xFFFFF) { 1.114 + // The n-byte UTF8 code unit will fit in two jschars. 1.115 + dst[j] = jschar((v >> 10) + 0xD800); 1.116 + j++; 1.117 + dst[j] = jschar((v & 0x3FF) + 0xDC00); 1.118 + } else { 1.119 + // The n-byte UTF8 code unit won't fit in two jschars. 1.120 + INVALID(ReportTooBigCharacter, v, 1); 1.121 + } 1.122 + } 1.123 + 1.124 + invalidMultiByteCodeUnit: 1.125 + // Move i to the last byte of the multi-byte code unit; the loop 1.126 + // header will do the final i++ to move to the start of the next 1.127 + // code unit. 1.128 + i += n - 1; 1.129 + } 1.130 + } 1.131 + 1.132 + dst[j] = 0; 1.133 + aOut = dst; 1.134 +} 1.135 + 1.136 +// Helper to check we have loaded the hardware shared library. 1.137 +#define CHECK_HWLIB(ret) \ 1.138 + void* hwLib = GetSharedLibrary(); \ 1.139 + if (!hwLib) { \ 1.140 + NS_WARNING("No /system/lib/libhardware_legacy.so"); \ 1.141 + return ret; \ 1.142 + } 1.143 + 1.144 +#define DEFAULT_IMPL(name, ret, args...) \ 1.145 + DEFINE_DLFUNC(name, ret, args...) \ 1.146 + ret do_##name(args) { \ 1.147 + USE_DLFUNC(name) \ 1.148 + return name(args); \ 1.149 + } 1.150 + 1.151 +// ICS implementation. 1.152 +class ICSWpaSupplicantImpl : public WpaSupplicantImpl 1.153 +{ 1.154 +public: 1.155 + DEFAULT_IMPL(wifi_load_driver, int32_t, ) 1.156 + DEFAULT_IMPL(wifi_unload_driver, int32_t, ) 1.157 + 1.158 + DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t) 1.159 + int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) { 1.160 + USE_DLFUNC(wifi_wait_for_event) 1.161 + return wifi_wait_for_event(buf, len); 1.162 + } 1.163 + 1.164 + DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) 1.165 + int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { 1.166 + USE_DLFUNC(wifi_command) 1.167 + return wifi_command(cmd, buf, len); 1.168 + } 1.169 + 1.170 + DEFINE_DLFUNC(wifi_start_supplicant, int32_t, ) 1.171 + int32_t do_wifi_start_supplicant(int32_t) { 1.172 + USE_DLFUNC(wifi_start_supplicant) 1.173 + return wifi_start_supplicant(); 1.174 + } 1.175 + 1.176 + DEFINE_DLFUNC(wifi_stop_supplicant, int32_t) 1.177 + int32_t do_wifi_stop_supplicant(int32_t) { 1.178 + USE_DLFUNC(wifi_stop_supplicant) 1.179 + return wifi_stop_supplicant(); 1.180 + } 1.181 + 1.182 + DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, ) 1.183 + int32_t do_wifi_connect_to_supplicant(const char* iface) { 1.184 + USE_DLFUNC(wifi_connect_to_supplicant) 1.185 + return wifi_connect_to_supplicant(); 1.186 + } 1.187 + 1.188 + DEFINE_DLFUNC(wifi_close_supplicant_connection, void, ) 1.189 + void do_wifi_close_supplicant_connection(const char* iface) { 1.190 + USE_DLFUNC(wifi_close_supplicant_connection) 1.191 + return wifi_close_supplicant_connection(); 1.192 + } 1.193 +}; 1.194 + 1.195 +// JB implementation. 1.196 +// We only redefine the methods that have a different signature than on ICS. 1.197 +class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl 1.198 +{ 1.199 +public: 1.200 + DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t) 1.201 + int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) { 1.202 + USE_DLFUNC(wifi_wait_for_event) 1.203 + return wifi_wait_for_event(iface, buf, len); 1.204 + } 1.205 + 1.206 + DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*) 1.207 + int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { 1.208 + USE_DLFUNC(wifi_command) 1.209 + return wifi_command(iface, cmd, buf, len); 1.210 + } 1.211 + 1.212 + DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) 1.213 + int32_t do_wifi_start_supplicant(int32_t arg) { 1.214 + USE_DLFUNC(wifi_start_supplicant) 1.215 + return wifi_start_supplicant(arg); 1.216 + } 1.217 + 1.218 + DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) 1.219 + int32_t do_wifi_stop_supplicant(int32_t arg) { 1.220 + USE_DLFUNC(wifi_stop_supplicant) 1.221 + return wifi_stop_supplicant(arg); 1.222 + } 1.223 + 1.224 + DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*) 1.225 + int32_t do_wifi_connect_to_supplicant(const char* iface) { 1.226 + USE_DLFUNC(wifi_connect_to_supplicant) 1.227 + return wifi_connect_to_supplicant(iface); 1.228 + } 1.229 + 1.230 + DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*) 1.231 + void do_wifi_close_supplicant_connection(const char* iface) { 1.232 + USE_DLFUNC(wifi_close_supplicant_connection) 1.233 + wifi_close_supplicant_connection(iface); 1.234 + } 1.235 +}; 1.236 + 1.237 +// KK implementation. 1.238 +// We only redefine the methods that have a different signature than on ICS. 1.239 +class KKWpaSupplicantImpl : public ICSWpaSupplicantImpl 1.240 +{ 1.241 +public: 1.242 + DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) 1.243 + int32_t do_wifi_start_supplicant(int32_t arg) { 1.244 + USE_DLFUNC(wifi_start_supplicant) 1.245 + return wifi_start_supplicant(arg); 1.246 + } 1.247 + 1.248 + DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) 1.249 + int32_t do_wifi_stop_supplicant(int32_t arg) { 1.250 + USE_DLFUNC(wifi_stop_supplicant) 1.251 + return wifi_stop_supplicant(arg); 1.252 + } 1.253 + 1.254 + DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) 1.255 + int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { 1.256 + char command[COMMAND_SIZE]; 1.257 + if (!strcmp(iface, "p2p0")) { 1.258 + // Commands for p2p0 interface don't need prefix 1.259 + snprintf(command, COMMAND_SIZE, "%s", cmd); 1.260 + } 1.261 + else { 1.262 + snprintf(command, COMMAND_SIZE, "IFNAME=%s %s", iface, cmd); 1.263 + } 1.264 + USE_DLFUNC(wifi_command) 1.265 + return wifi_command(command, buf, len); 1.266 + } 1.267 +}; 1.268 + 1.269 +// Concrete class to use to access the wpa supplicant. 1.270 +WpaSupplicant::WpaSupplicant() 1.271 +{ 1.272 + if (NetUtils::SdkVersion() < 16) { 1.273 + mImpl = new ICSWpaSupplicantImpl(); 1.274 + } else if (NetUtils::SdkVersion() < 19) { 1.275 + mImpl = new JBWpaSupplicantImpl(); 1.276 + } else { 1.277 + mImpl = new KKWpaSupplicantImpl(); 1.278 + } 1.279 + mNetUtils = new NetUtils(); 1.280 +}; 1.281 + 1.282 +void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface) 1.283 +{ 1.284 + CHECK_HWLIB() 1.285 + 1.286 + char buffer[BUFFER_SIZE]; 1.287 + int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE); 1.288 + CheckBuffer(buffer, ret, aEvent); 1.289 +} 1.290 + 1.291 +#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get() 1.292 + 1.293 +/** 1.294 + * Make a subnet mask. 1.295 + */ 1.296 +uint32_t WpaSupplicant::MakeMask(uint32_t len) { 1.297 + uint32_t mask = 0; 1.298 + for (uint32_t i = 0; i < len; ++i) { 1.299 + mask |= (0x80000000 >> i); 1.300 + } 1.301 + return ntohl(mask); 1.302 +} 1.303 + 1.304 +bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions, 1.305 + WifiResultOptions& aResult, 1.306 + const nsCString& aInterface) 1.307 +{ 1.308 + CHECK_HWLIB(false) 1.309 + if (!mNetUtils->GetSharedLibrary()) { 1.310 + return false; 1.311 + } 1.312 + 1.313 + // Always correlate the opaque ids. 1.314 + aResult.mId = aOptions.mId; 1.315 + 1.316 + if (aOptions.mCmd.EqualsLiteral("command")) { 1.317 + size_t len = BUFFER_SIZE - 1; 1.318 + char buffer[BUFFER_SIZE]; 1.319 + NS_ConvertUTF16toUTF8 request(aOptions.mRequest); 1.320 + aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len); 1.321 + nsString value; 1.322 + if (aResult.mStatus == 0) { 1.323 + if (buffer[len - 1] == '\n') { // remove trailing new lines. 1.324 + len--; 1.325 + } 1.326 + buffer[len] = '\0'; 1.327 + CheckBuffer(buffer, len, value); 1.328 + } 1.329 + aResult.mReply = value; 1.330 + } else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) { 1.331 + mImpl->do_wifi_close_supplicant_connection(aInterface.get()); 1.332 + } else if (aOptions.mCmd.EqualsLiteral("load_driver")) { 1.333 + aResult.mStatus = mImpl->do_wifi_load_driver(); 1.334 + } else if (aOptions.mCmd.EqualsLiteral("unload_driver")) { 1.335 + aResult.mStatus = mImpl->do_wifi_unload_driver(); 1.336 + } else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) { 1.337 + aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0); 1.338 + } else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) { 1.339 + aResult.mStatus = mImpl->do_wifi_stop_supplicant(0); 1.340 + } else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) { 1.341 + aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get()); 1.342 + } else if (aOptions.mCmd.EqualsLiteral("ifc_enable")) { 1.343 + aResult.mStatus = mNetUtils->do_ifc_enable(GET_CHAR(mIfname)); 1.344 + } else if (aOptions.mCmd.EqualsLiteral("ifc_disable")) { 1.345 + aResult.mStatus = mNetUtils->do_ifc_disable(GET_CHAR(mIfname)); 1.346 + } else if (aOptions.mCmd.EqualsLiteral("ifc_configure")) { 1.347 + aResult.mStatus = mNetUtils->do_ifc_configure( 1.348 + GET_CHAR(mIfname), aOptions.mIpaddr, aOptions.mMask, 1.349 + aOptions.mGateway, aOptions.mDns1, aOptions.mDns2 1.350 + ); 1.351 + } else if (aOptions.mCmd.EqualsLiteral("ifc_reset_connections")) { 1.352 + aResult.mStatus = mNetUtils->do_ifc_reset_connections( 1.353 + GET_CHAR(mIfname), RESET_ALL_ADDRESSES 1.354 + ); 1.355 + } else if (aOptions.mCmd.EqualsLiteral("dhcp_stop")) { 1.356 + aResult.mStatus = mNetUtils->do_dhcp_stop(GET_CHAR(mIfname)); 1.357 + } else if (aOptions.mCmd.EqualsLiteral("dhcp_do_request")) { 1.358 + char ipaddr[PROPERTY_VALUE_MAX]; 1.359 + char gateway[PROPERTY_VALUE_MAX]; 1.360 + uint32_t prefixLength; 1.361 + char dns1[PROPERTY_VALUE_MAX]; 1.362 + char dns2[PROPERTY_VALUE_MAX]; 1.363 + char server[PROPERTY_VALUE_MAX]; 1.364 + uint32_t lease; 1.365 + char vendorinfo[PROPERTY_VALUE_MAX]; 1.366 + aResult.mStatus = 1.367 + mNetUtils->do_dhcp_do_request(GET_CHAR(mIfname), 1.368 + ipaddr, 1.369 + gateway, 1.370 + &prefixLength, 1.371 + dns1, 1.372 + dns2, 1.373 + server, 1.374 + &lease, 1.375 + vendorinfo); 1.376 + 1.377 + if (aResult.mStatus == -1) { 1.378 + // Early return since we failed. 1.379 + return true; 1.380 + } 1.381 + 1.382 + aResult.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr); 1.383 + aResult.mGateway_str = NS_ConvertUTF8toUTF16(gateway); 1.384 + aResult.mDns1_str = NS_ConvertUTF8toUTF16(dns1); 1.385 + aResult.mDns2_str = NS_ConvertUTF8toUTF16(dns2); 1.386 + aResult.mServer_str = NS_ConvertUTF8toUTF16(server); 1.387 + aResult.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo); 1.388 + aResult.mLease = lease; 1.389 + aResult.mMask = MakeMask(prefixLength); 1.390 + 1.391 + uint32_t inet4; // only support IPv4 for now. 1.392 + 1.393 +#define INET_PTON(var, field) \ 1.394 + PR_BEGIN_MACRO \ 1.395 + inet_pton(AF_INET, var, &inet4); \ 1.396 + aResult.field = inet4; \ 1.397 + PR_END_MACRO 1.398 + 1.399 + INET_PTON(ipaddr, mIpaddr); 1.400 + INET_PTON(gateway, mGateway); 1.401 + 1.402 + if (dns1[0] != '\0') { 1.403 + INET_PTON(dns1, mDns1); 1.404 + } 1.405 + 1.406 + if (dns2[0] != '\0') { 1.407 + INET_PTON(dns2, mDns2); 1.408 + } 1.409 + 1.410 + INET_PTON(server, mServer); 1.411 + 1.412 + //aResult.mask_str = netHelpers.ipToString(obj.mask); 1.413 + char inet_str[64]; 1.414 + if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) { 1.415 + aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str); 1.416 + } 1.417 + } else { 1.418 + NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command"); 1.419 + printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s", 1.420 + NS_ConvertUTF16toUTF8(aOptions.mCmd).get()); 1.421 + return false; 1.422 + } 1.423 + 1.424 + return true; 1.425 +} 1.426 + 1.427 +// Checks the buffer and do the utf processing. 1.428 +void 1.429 +WpaSupplicant::CheckBuffer(char* buffer, int32_t length, 1.430 + nsAString& aEvent) 1.431 +{ 1.432 + if (length > 0 && length < BUFFER_SIZE) { 1.433 + buffer[length] = 0; 1.434 + LossyConvertUTF8toUTF16(buffer, length, aEvent); 1.435 + } 1.436 +}