michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WifiUtils.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include "prinit.h" michael@0: #include "js/CharacterEncoding.h" michael@0: #include "mozilla/dom/network/NetUtils.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: #define BUFFER_SIZE 4096 michael@0: #define COMMAND_SIZE 256 michael@0: #define PROPERTY_VALUE_MAX 80 michael@0: michael@0: // Intentionally not trying to dlclose() this handle. That's playing michael@0: // Russian roulette with security bugs. michael@0: static void* sWifiLib; michael@0: static PRCallOnceType sInitWifiLib; michael@0: michael@0: static PRStatus michael@0: InitWifiLib() michael@0: { michael@0: sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY); michael@0: // We might fail to open the hardware lib. That's OK. michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: static void* michael@0: GetSharedLibrary() michael@0: { michael@0: PR_CallOnce(&sInitWifiLib, InitWifiLib); michael@0: return sWifiLib; michael@0: } michael@0: michael@0: static bool michael@0: GetWifiP2pSupported() michael@0: { michael@0: char propP2pSupported[PROPERTY_VALUE_MAX]; michael@0: property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0"); michael@0: return (0 == strcmp(propP2pSupported, "1")); michael@0: } michael@0: michael@0: // This is the same algorithm as in InflateUTF8StringToBuffer with Copy and michael@0: // while ignoring invalids. michael@0: // https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231 michael@0: michael@0: static const uint32_t REPLACE_UTF8 = 0xFFFD; michael@0: michael@0: void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut) michael@0: { michael@0: JS::UTF8Chars src(aInput, aLength); michael@0: michael@0: char16_t dst[aLength]; // Allocating for worst case. michael@0: michael@0: // First, count how many jschars need to be in the inflated string. michael@0: // |i| is the index into |src|, and |j| is the the index into |dst|. michael@0: size_t srclen = src.length(); michael@0: uint32_t j = 0; michael@0: for (uint32_t i = 0; i < srclen; i++, j++) { michael@0: uint32_t v = uint32_t(src[i]); michael@0: if (!(v & 0x80)) { michael@0: // ASCII code unit. Simple copy. michael@0: dst[j] = char16_t(v); michael@0: } else { michael@0: // Non-ASCII code unit. Determine its length in bytes (n). michael@0: uint32_t n = 1; michael@0: while (v & (0x80 >> n)) michael@0: n++; michael@0: michael@0: #define INVALID(report, arg, n2) \ michael@0: do { \ michael@0: n = n2; \ michael@0: goto invalidMultiByteCodeUnit; \ michael@0: } while (0) michael@0: michael@0: // Check the leading byte. michael@0: if (n < 2 || n > 4) michael@0: INVALID(ReportInvalidCharacter, i, 1); michael@0: michael@0: // Check that |src| is large enough to hold an n-byte code unit. michael@0: if (i + n > srclen) michael@0: INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1); michael@0: michael@0: // Check the second byte. From Unicode Standard v6.2, Table 3-7 michael@0: // Well-Formed UTF-8 Byte Sequences. michael@0: if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) || // E0 A0~BF michael@0: (v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) || // ED 80~9F michael@0: (v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) || // F0 90~BF michael@0: (v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80)) // F4 80~8F michael@0: { michael@0: INVALID(ReportInvalidCharacter, i, 1); michael@0: } michael@0: michael@0: // Check the continuation bytes. michael@0: for (uint32_t m = 1; m < n; m++) michael@0: if ((src[i + m] & 0xC0) != 0x80) michael@0: INVALID(ReportInvalidCharacter, i, m); michael@0: michael@0: // Determine the code unit's length in jschars and act accordingly. michael@0: v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n); michael@0: if (v < 0x10000) { michael@0: // The n-byte UTF8 code unit will fit in a single jschar. michael@0: dst[j] = jschar(v); michael@0: } else { michael@0: v -= 0x10000; michael@0: if (v <= 0xFFFFF) { michael@0: // The n-byte UTF8 code unit will fit in two jschars. michael@0: dst[j] = jschar((v >> 10) + 0xD800); michael@0: j++; michael@0: dst[j] = jschar((v & 0x3FF) + 0xDC00); michael@0: } else { michael@0: // The n-byte UTF8 code unit won't fit in two jschars. michael@0: INVALID(ReportTooBigCharacter, v, 1); michael@0: } michael@0: } michael@0: michael@0: invalidMultiByteCodeUnit: michael@0: // Move i to the last byte of the multi-byte code unit; the loop michael@0: // header will do the final i++ to move to the start of the next michael@0: // code unit. michael@0: i += n - 1; michael@0: } michael@0: } michael@0: michael@0: dst[j] = 0; michael@0: aOut = dst; michael@0: } michael@0: michael@0: // Helper to check we have loaded the hardware shared library. michael@0: #define CHECK_HWLIB(ret) \ michael@0: void* hwLib = GetSharedLibrary(); \ michael@0: if (!hwLib) { \ michael@0: NS_WARNING("No /system/lib/libhardware_legacy.so"); \ michael@0: return ret; \ michael@0: } michael@0: michael@0: #define DEFAULT_IMPL(name, ret, args...) \ michael@0: DEFINE_DLFUNC(name, ret, args...) \ michael@0: ret do_##name(args) { \ michael@0: USE_DLFUNC(name) \ michael@0: return name(args); \ michael@0: } michael@0: michael@0: // ICS implementation. michael@0: class ICSWpaSupplicantImpl : public WpaSupplicantImpl michael@0: { michael@0: public: michael@0: DEFAULT_IMPL(wifi_load_driver, int32_t, ) michael@0: DEFAULT_IMPL(wifi_unload_driver, int32_t, ) michael@0: michael@0: DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t) michael@0: int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) { michael@0: USE_DLFUNC(wifi_wait_for_event) michael@0: return wifi_wait_for_event(buf, len); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) michael@0: int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { michael@0: USE_DLFUNC(wifi_command) michael@0: return wifi_command(cmd, buf, len); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_start_supplicant, int32_t, ) michael@0: int32_t do_wifi_start_supplicant(int32_t) { michael@0: USE_DLFUNC(wifi_start_supplicant) michael@0: return wifi_start_supplicant(); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_stop_supplicant, int32_t) michael@0: int32_t do_wifi_stop_supplicant(int32_t) { michael@0: USE_DLFUNC(wifi_stop_supplicant) michael@0: return wifi_stop_supplicant(); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, ) michael@0: int32_t do_wifi_connect_to_supplicant(const char* iface) { michael@0: USE_DLFUNC(wifi_connect_to_supplicant) michael@0: return wifi_connect_to_supplicant(); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_close_supplicant_connection, void, ) michael@0: void do_wifi_close_supplicant_connection(const char* iface) { michael@0: USE_DLFUNC(wifi_close_supplicant_connection) michael@0: return wifi_close_supplicant_connection(); michael@0: } michael@0: }; michael@0: michael@0: // JB implementation. michael@0: // We only redefine the methods that have a different signature than on ICS. michael@0: class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl michael@0: { michael@0: public: michael@0: DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t) michael@0: int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) { michael@0: USE_DLFUNC(wifi_wait_for_event) michael@0: return wifi_wait_for_event(iface, buf, len); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*) michael@0: int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { michael@0: USE_DLFUNC(wifi_command) michael@0: return wifi_command(iface, cmd, buf, len); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) michael@0: int32_t do_wifi_start_supplicant(int32_t arg) { michael@0: USE_DLFUNC(wifi_start_supplicant) michael@0: return wifi_start_supplicant(arg); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) michael@0: int32_t do_wifi_stop_supplicant(int32_t arg) { michael@0: USE_DLFUNC(wifi_stop_supplicant) michael@0: return wifi_stop_supplicant(arg); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*) michael@0: int32_t do_wifi_connect_to_supplicant(const char* iface) { michael@0: USE_DLFUNC(wifi_connect_to_supplicant) michael@0: return wifi_connect_to_supplicant(iface); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*) michael@0: void do_wifi_close_supplicant_connection(const char* iface) { michael@0: USE_DLFUNC(wifi_close_supplicant_connection) michael@0: wifi_close_supplicant_connection(iface); michael@0: } michael@0: }; michael@0: michael@0: // KK implementation. michael@0: // We only redefine the methods that have a different signature than on ICS. michael@0: class KKWpaSupplicantImpl : public ICSWpaSupplicantImpl michael@0: { michael@0: public: michael@0: DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t) michael@0: int32_t do_wifi_start_supplicant(int32_t arg) { michael@0: USE_DLFUNC(wifi_start_supplicant) michael@0: return wifi_start_supplicant(arg); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t) michael@0: int32_t do_wifi_stop_supplicant(int32_t arg) { michael@0: USE_DLFUNC(wifi_stop_supplicant) michael@0: return wifi_stop_supplicant(arg); michael@0: } michael@0: michael@0: DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*) michael@0: int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) { michael@0: char command[COMMAND_SIZE]; michael@0: if (!strcmp(iface, "p2p0")) { michael@0: // Commands for p2p0 interface don't need prefix michael@0: snprintf(command, COMMAND_SIZE, "%s", cmd); michael@0: } michael@0: else { michael@0: snprintf(command, COMMAND_SIZE, "IFNAME=%s %s", iface, cmd); michael@0: } michael@0: USE_DLFUNC(wifi_command) michael@0: return wifi_command(command, buf, len); michael@0: } michael@0: }; michael@0: michael@0: // Concrete class to use to access the wpa supplicant. michael@0: WpaSupplicant::WpaSupplicant() michael@0: { michael@0: if (NetUtils::SdkVersion() < 16) { michael@0: mImpl = new ICSWpaSupplicantImpl(); michael@0: } else if (NetUtils::SdkVersion() < 19) { michael@0: mImpl = new JBWpaSupplicantImpl(); michael@0: } else { michael@0: mImpl = new KKWpaSupplicantImpl(); michael@0: } michael@0: mNetUtils = new NetUtils(); michael@0: }; michael@0: michael@0: void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface) michael@0: { michael@0: CHECK_HWLIB() michael@0: michael@0: char buffer[BUFFER_SIZE]; michael@0: int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE); michael@0: CheckBuffer(buffer, ret, aEvent); michael@0: } michael@0: michael@0: #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get() michael@0: michael@0: /** michael@0: * Make a subnet mask. michael@0: */ michael@0: uint32_t WpaSupplicant::MakeMask(uint32_t len) { michael@0: uint32_t mask = 0; michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: mask |= (0x80000000 >> i); michael@0: } michael@0: return ntohl(mask); michael@0: } michael@0: michael@0: bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions, michael@0: WifiResultOptions& aResult, michael@0: const nsCString& aInterface) michael@0: { michael@0: CHECK_HWLIB(false) michael@0: if (!mNetUtils->GetSharedLibrary()) { michael@0: return false; michael@0: } michael@0: michael@0: // Always correlate the opaque ids. michael@0: aResult.mId = aOptions.mId; michael@0: michael@0: if (aOptions.mCmd.EqualsLiteral("command")) { michael@0: size_t len = BUFFER_SIZE - 1; michael@0: char buffer[BUFFER_SIZE]; michael@0: NS_ConvertUTF16toUTF8 request(aOptions.mRequest); michael@0: aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len); michael@0: nsString value; michael@0: if (aResult.mStatus == 0) { michael@0: if (buffer[len - 1] == '\n') { // remove trailing new lines. michael@0: len--; michael@0: } michael@0: buffer[len] = '\0'; michael@0: CheckBuffer(buffer, len, value); michael@0: } michael@0: aResult.mReply = value; michael@0: } else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) { michael@0: mImpl->do_wifi_close_supplicant_connection(aInterface.get()); michael@0: } else if (aOptions.mCmd.EqualsLiteral("load_driver")) { michael@0: aResult.mStatus = mImpl->do_wifi_load_driver(); michael@0: } else if (aOptions.mCmd.EqualsLiteral("unload_driver")) { michael@0: aResult.mStatus = mImpl->do_wifi_unload_driver(); michael@0: } else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) { michael@0: aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0); michael@0: } else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) { michael@0: aResult.mStatus = mImpl->do_wifi_stop_supplicant(0); michael@0: } else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) { michael@0: aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get()); michael@0: } else if (aOptions.mCmd.EqualsLiteral("ifc_enable")) { michael@0: aResult.mStatus = mNetUtils->do_ifc_enable(GET_CHAR(mIfname)); michael@0: } else if (aOptions.mCmd.EqualsLiteral("ifc_disable")) { michael@0: aResult.mStatus = mNetUtils->do_ifc_disable(GET_CHAR(mIfname)); michael@0: } else if (aOptions.mCmd.EqualsLiteral("ifc_configure")) { michael@0: aResult.mStatus = mNetUtils->do_ifc_configure( michael@0: GET_CHAR(mIfname), aOptions.mIpaddr, aOptions.mMask, michael@0: aOptions.mGateway, aOptions.mDns1, aOptions.mDns2 michael@0: ); michael@0: } else if (aOptions.mCmd.EqualsLiteral("ifc_reset_connections")) { michael@0: aResult.mStatus = mNetUtils->do_ifc_reset_connections( michael@0: GET_CHAR(mIfname), RESET_ALL_ADDRESSES michael@0: ); michael@0: } else if (aOptions.mCmd.EqualsLiteral("dhcp_stop")) { michael@0: aResult.mStatus = mNetUtils->do_dhcp_stop(GET_CHAR(mIfname)); michael@0: } else if (aOptions.mCmd.EqualsLiteral("dhcp_do_request")) { michael@0: char ipaddr[PROPERTY_VALUE_MAX]; michael@0: char gateway[PROPERTY_VALUE_MAX]; michael@0: uint32_t prefixLength; michael@0: char dns1[PROPERTY_VALUE_MAX]; michael@0: char dns2[PROPERTY_VALUE_MAX]; michael@0: char server[PROPERTY_VALUE_MAX]; michael@0: uint32_t lease; michael@0: char vendorinfo[PROPERTY_VALUE_MAX]; michael@0: aResult.mStatus = michael@0: mNetUtils->do_dhcp_do_request(GET_CHAR(mIfname), michael@0: ipaddr, michael@0: gateway, michael@0: &prefixLength, michael@0: dns1, michael@0: dns2, michael@0: server, michael@0: &lease, michael@0: vendorinfo); michael@0: michael@0: if (aResult.mStatus == -1) { michael@0: // Early return since we failed. michael@0: return true; michael@0: } michael@0: michael@0: aResult.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr); michael@0: aResult.mGateway_str = NS_ConvertUTF8toUTF16(gateway); michael@0: aResult.mDns1_str = NS_ConvertUTF8toUTF16(dns1); michael@0: aResult.mDns2_str = NS_ConvertUTF8toUTF16(dns2); michael@0: aResult.mServer_str = NS_ConvertUTF8toUTF16(server); michael@0: aResult.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo); michael@0: aResult.mLease = lease; michael@0: aResult.mMask = MakeMask(prefixLength); michael@0: michael@0: uint32_t inet4; // only support IPv4 for now. michael@0: michael@0: #define INET_PTON(var, field) \ michael@0: PR_BEGIN_MACRO \ michael@0: inet_pton(AF_INET, var, &inet4); \ michael@0: aResult.field = inet4; \ michael@0: PR_END_MACRO michael@0: michael@0: INET_PTON(ipaddr, mIpaddr); michael@0: INET_PTON(gateway, mGateway); michael@0: michael@0: if (dns1[0] != '\0') { michael@0: INET_PTON(dns1, mDns1); michael@0: } michael@0: michael@0: if (dns2[0] != '\0') { michael@0: INET_PTON(dns2, mDns2); michael@0: } michael@0: michael@0: INET_PTON(server, mServer); michael@0: michael@0: //aResult.mask_str = netHelpers.ipToString(obj.mask); michael@0: char inet_str[64]; michael@0: if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) { michael@0: aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str); michael@0: } michael@0: } else { michael@0: NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command"); michael@0: printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s", michael@0: NS_ConvertUTF16toUTF8(aOptions.mCmd).get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Checks the buffer and do the utf processing. michael@0: void michael@0: WpaSupplicant::CheckBuffer(char* buffer, int32_t length, michael@0: nsAString& aEvent) michael@0: { michael@0: if (length > 0 && length < BUFFER_SIZE) { michael@0: buffer[length] = 0; michael@0: LossyConvertUTF8toUTF16(buffer, length, aEvent); michael@0: } michael@0: }