Fix parser forgetting all non-owned groups
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package be.mygod.vpnhotspot
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
@@ -16,4 +17,9 @@ abstract class AlertDialogFragment : DialogFragment(), DialogInterface.OnClickLi
|
||||
override fun onClick(dialog: DialogInterface?, which: Int) {
|
||||
targetFragment!!.onActivityResult(targetRequestCode, which, data)
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface?) {
|
||||
super.onDismiss(dialog)
|
||||
targetFragment!!.onActivityResult(targetRequestCode, Activity.RESULT_CANCELED, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
groupChanged(value)
|
||||
}
|
||||
val groupChanged = StickyEvent1 { group }
|
||||
var thisDevice: WifiP2pDevice? = null
|
||||
|
||||
fun startWps(pin: String? = null) {
|
||||
val channel = channel
|
||||
@@ -113,17 +114,15 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP))
|
||||
}
|
||||
}
|
||||
private var routingManager: LocalOnlyInterfaceManager? = null
|
||||
|
||||
private var thisDevice: WifiP2pDevice? = null
|
||||
private val deviceListener = broadcastReceiver { _, intent ->
|
||||
when (intent.action) {
|
||||
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
|
||||
thisDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)
|
||||
binder.thisDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)
|
||||
}
|
||||
WifiP2pManagerHelper.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION -> onPersistentGroupsChanged()
|
||||
}
|
||||
}
|
||||
private var routingManager: LocalOnlyInterfaceManager? = null
|
||||
|
||||
var status = Status.IDLE
|
||||
private set(value) {
|
||||
@@ -188,7 +187,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, SharedPrefere
|
||||
|
||||
private fun onPersistentGroupsChanged() {
|
||||
val channel = channel ?: return
|
||||
val device = thisDevice ?: return
|
||||
val device = binder.thisDevice ?: return
|
||||
try {
|
||||
p2pManager.requestPersistentGroupInfo(channel) {
|
||||
val ownedGroups = it.filter { it.isGroupOwner && it.owner.deviceAddress == device.deviceAddress }
|
||||
|
||||
@@ -15,6 +15,9 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.databinding.BaseObservable
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.lifecycle.get
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import be.mygod.vpnhotspot.*
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
@@ -25,6 +28,7 @@ import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.util.formatAddresses
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import timber.log.Timber
|
||||
import java.lang.RuntimeException
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
@@ -89,23 +93,20 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
}
|
||||
|
||||
fun editConfigurations() {
|
||||
val binder = binder
|
||||
val group = binder?.group
|
||||
val ssid = group?.networkName
|
||||
if (ssid != null) {
|
||||
val wifi = WifiConfiguration()
|
||||
val conf = P2pSupplicantConfiguration()
|
||||
wifi.SSID = ssid
|
||||
wifi.preSharedKey = group.passphrase
|
||||
if (wifi.preSharedKey == null) wifi.preSharedKey = conf.readPsk()
|
||||
if (wifi.preSharedKey != null) {
|
||||
if (group != null) try {
|
||||
val config = P2pSupplicantConfiguration(group, binder?.thisDevice?.deviceAddress)
|
||||
holder.config = config
|
||||
WifiP2pDialogFragment().apply {
|
||||
arguments = bundleOf(Pair(WifiP2pDialogFragment.KEY_CONFIGURATION, wifi),
|
||||
Pair(WifiP2pDialogFragment.KEY_CONFIGURER, conf))
|
||||
arguments = bundleOf(Pair(WifiP2pDialogFragment.KEY_CONFIGURATION, WifiConfiguration().apply {
|
||||
SSID = config.ssid
|
||||
preSharedKey = config.psk
|
||||
}))
|
||||
setTargetFragment(parent, TetheringFragment.REPEATER_EDIT_CONFIGURATION)
|
||||
}.show(parent.fragmentManager, WifiP2pDialogFragment.TAG)
|
||||
return
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
Timber.w(e)
|
||||
}
|
||||
SmartSnackbar.make(R.string.repeater_configure_failure).show()
|
||||
}
|
||||
@@ -131,6 +132,10 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigHolder : ViewModel() {
|
||||
var config: P2pSupplicantConfiguration? = null
|
||||
}
|
||||
|
||||
init {
|
||||
ServiceForegroundConnector(parent, this, RepeaterService::class)
|
||||
}
|
||||
@@ -139,6 +144,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
private val data = Data()
|
||||
internal var binder: RepeaterService.Binder? = null
|
||||
private var p2pInterface: String? = null
|
||||
private val holder = ViewModelProviders.of(parent).get<ConfigHolder>()
|
||||
|
||||
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
||||
(viewHolder as ViewHolder).binding.data = data
|
||||
@@ -169,8 +175,9 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
fun onEditResult(which: Int, data: Intent) {
|
||||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> try {
|
||||
data.getParcelableExtra<P2pSupplicantConfiguration>(WifiP2pDialogFragment.KEY_CONFIGURER)
|
||||
.update(data.getParcelableExtra(WifiP2pDialogFragment.KEY_CONFIGURATION))
|
||||
val master = holder.config ?: return
|
||||
val config = data.getParcelableExtra<WifiConfiguration>(WifiP2pDialogFragment.KEY_CONFIGURATION)
|
||||
master.update(config.SSID, config.preSharedKey)
|
||||
binder!!.group = null
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e)
|
||||
@@ -178,5 +185,6 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
}
|
||||
DialogInterface.BUTTON_NEUTRAL -> binder!!.resetCredentials()
|
||||
}
|
||||
holder.config = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,94 @@
|
||||
package be.mygod.vpnhotspot.net.wifi
|
||||
|
||||
import android.net.wifi.WifiConfiguration
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.util.RootSession
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class P2pSupplicantConfiguration(private val initContent: String? = null) : Parcelable {
|
||||
companion object CREATOR : Parcelable.Creator<P2pSupplicantConfiguration> {
|
||||
override fun createFromParcel(parcel: Parcel) = P2pSupplicantConfiguration(parcel.readString())
|
||||
override fun newArray(size: Int): Array<P2pSupplicantConfiguration?> = arrayOfNulls(size)
|
||||
|
||||
/**
|
||||
* Format for ssid is much more complicated, therefore we are only trying to find the line and rely on
|
||||
* Android's results instead.
|
||||
*
|
||||
* Source: https://android.googlesource.com/platform/external/wpa_supplicant_8/+/2933359/src/utils/common.c#631
|
||||
/**
|
||||
* This parser is based on:
|
||||
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488
|
||||
* https://android.googlesource.com/platform/external/wpa_supplicant_8/+/6fa46df/wpa_supplicant/config_file.c#182
|
||||
*/
|
||||
private val ssidMatcher = "^[\\r\\t ]*ssid=".toRegex()
|
||||
/**
|
||||
* PSK parser can be found here: https://android.googlesource.com/platform/external/wpa_supplicant_8/+/d2986c2/wpa_supplicant/config.c#488
|
||||
*/
|
||||
private val pskParser = "^[\\r\\t ]*psk=(ext:|\"(.*)\"|\"(.*)|[0-9a-fA-F]{64}\$)".toRegex(RegexOption.MULTILINE)
|
||||
class P2pSupplicantConfiguration(private val group: WifiP2pGroup, ownerAddress: String?) {
|
||||
companion object {
|
||||
private val networkParser =
|
||||
"^(bssid=(([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})|psk=(ext:|\"(.*)\"|[0-9a-fA-F]{64}\$))".toRegex()
|
||||
private val whitespaceMatcher = "\\s+".toRegex()
|
||||
|
||||
private val confPath = if (Build.VERSION.SDK_INT >= 28)
|
||||
"/data/vendor/wifi/wpa/p2p_supplicant.conf" else "/data/misc/wifi/p2p_supplicant.conf"
|
||||
}
|
||||
private class InvalidConfigurationError : IOException("Invalid configuration")
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
out.writeString(if (contentDelegate.isInitialized()) content else null)
|
||||
}
|
||||
override fun describeContents() = 0
|
||||
private class NetworkBlock : ArrayList<String>() {
|
||||
var ssidLine: Int? = null
|
||||
var pskLine: Int? = null
|
||||
var psk: String? = null
|
||||
var groupOwner = false
|
||||
var bssidMatches = false
|
||||
|
||||
private val contentDelegate = lazy { initContent ?: RootSession.use { it.execOut("cat $confPath") } }
|
||||
private val content by contentDelegate
|
||||
|
||||
fun readPsk(): String? {
|
||||
return try {
|
||||
val match = pskParser.findAll(content).single()
|
||||
if (match.groups[2] == null && match.groups[3] == null) "" else {
|
||||
// only one will match and hold non-empty value
|
||||
val result = match.groupValues[2] + match.groupValues[3]
|
||||
check(result.length in 8..63)
|
||||
result
|
||||
}
|
||||
} catch (e: NoSuchElementException) {
|
||||
null
|
||||
} catch (e: RuntimeException) {
|
||||
if (contentDelegate.isInitialized()) Timber.w(content)
|
||||
Timber.w(e)
|
||||
null
|
||||
}
|
||||
override fun toString() = joinToString("\n")
|
||||
}
|
||||
|
||||
fun update(config: WifiConfiguration) {
|
||||
private class Parser(lines: List<String>) {
|
||||
private val iterator = lines.iterator()
|
||||
lateinit var line: String
|
||||
lateinit var trimmed: String
|
||||
fun next() = if (iterator.hasNext()) {
|
||||
line = iterator.next().apply { trimmed = trimStart('\r', '\t', ' ') }
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
private val content by lazy {
|
||||
RootSession.use {
|
||||
val result = ArrayList<Any>()
|
||||
var target: NetworkBlock? = null
|
||||
val parser = Parser(it.execOutUnjoined("cat $confPath"))
|
||||
while (parser.next()) {
|
||||
if (parser.trimmed.startsWith("network={")) {
|
||||
val block = NetworkBlock()
|
||||
block.add(parser.line)
|
||||
while (parser.next() && !parser.trimmed.startsWith('}')) {
|
||||
if (parser.trimmed.startsWith("ssid=")) {
|
||||
check(block.ssidLine == null)
|
||||
block.ssidLine = block.size
|
||||
} else if (parser.trimmed.startsWith("mode=3")) block.groupOwner = true else {
|
||||
val match = networkParser.find(parser.trimmed)
|
||||
if (match != null) if (match.groups[5] != null) {
|
||||
check(block.pskLine == null && block.psk == null)
|
||||
block.psk = match.groupValues[5].apply { check(length in 8..63) }
|
||||
block.pskLine = block.size
|
||||
} else if (match.groups[2] != null &&
|
||||
match.groupValues[2].equals(group.owner.deviceAddress ?: ownerAddress, true)) {
|
||||
block.bssidMatches = true
|
||||
}
|
||||
}
|
||||
block.add(parser.line)
|
||||
}
|
||||
block.add(parser.line)
|
||||
result.add(block)
|
||||
if (block.bssidMatches && block.groupOwner && target == null) { // keep first only
|
||||
check(block.ssidLine != null && block.pskLine != null)
|
||||
target = block
|
||||
}
|
||||
} else result.add(parser.line)
|
||||
}
|
||||
Pair(result, target!!)
|
||||
}
|
||||
}
|
||||
val ssid = group.networkName
|
||||
val psk = group.passphrase ?: content.second.psk!!
|
||||
|
||||
fun update(ssid: String, psk: String) {
|
||||
val (lines, block) = content
|
||||
block[block.ssidLine!!] = "\tssid=" + ssid.toByteArray()
|
||||
.joinToString("") { (it.toInt() and 255).toString(16).padStart(2, '0') }
|
||||
block[block.pskLine!!] = "\tpsk=\"$psk\"" // no control chars or weird stuff
|
||||
val tempFile = File.createTempFile("vpnhotspot-", ".conf", app.cacheDir)
|
||||
try {
|
||||
var ssidFound = 0
|
||||
var pskFound = 0
|
||||
tempFile.printWriter().use {
|
||||
for (line in content.lineSequence()) it.println(when {
|
||||
ssidMatcher.containsMatchIn(line) -> {
|
||||
ssidFound += 1
|
||||
"\tssid=" + config.SSID.toByteArray()
|
||||
.joinToString("") { (it.toInt() and 255).toString(16).padStart(2, '0') }
|
||||
}
|
||||
pskParser.containsMatchIn(line) -> {
|
||||
pskFound += 1
|
||||
"\tpsk=\"${config.preSharedKey}\"" // no control chars or weird stuff
|
||||
}
|
||||
else -> line // do nothing
|
||||
})
|
||||
}
|
||||
if (ssidFound != 1 || pskFound != 1) {
|
||||
Timber.w("Invalid conf ($ssidFound, $pskFound): $content")
|
||||
if (ssidFound == 0 || pskFound == 0) throw InvalidConfigurationError()
|
||||
else Timber.i(InvalidConfigurationError())
|
||||
tempFile.printWriter().use { writer ->
|
||||
lines.forEach { writer.println(it) }
|
||||
}
|
||||
// pkill not available on Lollipop. Source: https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md
|
||||
RootSession.use {
|
||||
|
||||
@@ -25,13 +25,11 @@ class WifiP2pDialogFragment : AlertDialogFragment(), TextWatcher, DialogInterfac
|
||||
companion object {
|
||||
const val TAG = "WifiP2pDialogFragment"
|
||||
const val KEY_CONFIGURATION = "configuration"
|
||||
const val KEY_CONFIGURER = "configurer"
|
||||
}
|
||||
|
||||
private lateinit var mView: View
|
||||
private lateinit var mSsid: TextView
|
||||
private lateinit var mPassword: EditText
|
||||
private lateinit var configurer: P2pSupplicantConfiguration
|
||||
private val config: WifiConfiguration?
|
||||
get() {
|
||||
val config = WifiConfiguration()
|
||||
@@ -54,7 +52,6 @@ class WifiP2pDialogFragment : AlertDialogFragment(), TextWatcher, DialogInterfac
|
||||
setNegativeButton(context.getString(R.string.wifi_cancel), null)
|
||||
setNeutralButton(context.getString(R.string.repeater_reset_credentials), listener)
|
||||
val arguments = arguments!!
|
||||
configurer = arguments.getParcelable(KEY_CONFIGURER)!!
|
||||
val mWifiConfig = arguments.getParcelable<WifiConfiguration>(KEY_CONFIGURATION)
|
||||
if (mWifiConfig != null) {
|
||||
mSsid.text = mWifiConfig.SSID
|
||||
@@ -66,7 +63,6 @@ class WifiP2pDialogFragment : AlertDialogFragment(), TextWatcher, DialogInterfac
|
||||
|
||||
override val data get() = Intent().apply {
|
||||
putExtra(KEY_CONFIGURATION, config)
|
||||
putExtra(KEY_CONFIGURER, configurer)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
||||
Reference in New Issue
Block a user