mirror of
https://github.com/yggdrasil-network/yggdrasil-android.git
synced 2025-04-28 22:25:09 +03:00
Added DNS configuration functionality. (#24)
* Changed app icon from default to Yggdrasil leaf. * Added workaround for DNS-reslver and fix for unmetered networks. * Added DNS configuration functionality. * Changed DNS configuration UI. Disabled DNS config by default. Added DNS fix for Chrome-based browsers.
This commit is contained in:
parent
24573625a4
commit
772dfdef8c
16 changed files with 620 additions and 26 deletions
166
app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt
Normal file
166
app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt
Normal file
|
@ -0,0 +1,166 @@
|
|||
package eu.neilalexander.yggdrasil
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.SharedPreferences
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
|
||||
const val KEY_DNS_SERVERS = "dns_servers"
|
||||
const val KEY_ENABLE_CHROME_FIX = "enable_chrome_fix"
|
||||
const val DEFAULT_DNS_SERVERS = "302:7991::53,302:db60::53,300:6223::53,301:1088::53"
|
||||
|
||||
class DnsActivity : AppCompatActivity() {
|
||||
private lateinit var config: ConfigurationProxy
|
||||
private lateinit var inflater: LayoutInflater
|
||||
|
||||
private lateinit var serversTableLayout: TableLayout
|
||||
private lateinit var serversTableLabel: TextView
|
||||
private lateinit var serversTableHint: TextView
|
||||
private lateinit var addServerButton: ImageButton
|
||||
private lateinit var enableChromeFix: Switch
|
||||
|
||||
private lateinit var servers: MutableList<String>
|
||||
private lateinit var preferences: SharedPreferences
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_dns)
|
||||
|
||||
config = ConfigurationProxy(applicationContext)
|
||||
inflater = LayoutInflater.from(this)
|
||||
|
||||
serversTableLayout = findViewById(R.id.configuredDnsTableLayout)
|
||||
serversTableLabel = findViewById(R.id.configuredDnsLabel)
|
||||
serversTableHint = findViewById(R.id.configuredDnsHint)
|
||||
enableChromeFix = findViewById(R.id.enableChromeFix)
|
||||
|
||||
addServerButton = findViewById(R.id.addServerButton)
|
||||
addServerButton.setOnClickListener {
|
||||
val view = inflater.inflate(R.layout.dialog_add_dns_server, null)
|
||||
val input = view.findViewById<TextInputEditText>(R.id.addDnsInput)
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
||||
builder.setTitle(getString(R.string.dns_add_server_dialog_title))
|
||||
builder.setView(view)
|
||||
builder.setPositiveButton(getString(R.string.add)) { dialog, _ ->
|
||||
servers.add(input.text.toString())
|
||||
preferences.edit().apply {
|
||||
putString(KEY_DNS_SERVERS, servers.joinToString(","))
|
||||
commit()
|
||||
}
|
||||
dialog.dismiss()
|
||||
updateConfiguredServers()
|
||||
}
|
||||
builder.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
|
||||
enableChromeFix.setOnCheckedChangeListener { _, isChecked ->
|
||||
preferences.edit().apply {
|
||||
putBoolean(KEY_ENABLE_CHROME_FIX, isChecked)
|
||||
commit()
|
||||
}
|
||||
}
|
||||
|
||||
preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
|
||||
servers = if (serverString!!.isNotEmpty()) {
|
||||
serverString.split(",").toMutableList()
|
||||
} else {
|
||||
mutableListOf()
|
||||
}
|
||||
updateUsableServers()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateConfiguredServers()
|
||||
enableChromeFix.isChecked = preferences.getBoolean(KEY_ENABLE_CHROME_FIX, false)
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun updateConfiguredServers() {
|
||||
when (servers.size) {
|
||||
0 -> {
|
||||
serversTableLayout.visibility = View.GONE
|
||||
serversTableLabel.text = getString(R.string.dns_no_configured_servers)
|
||||
serversTableHint.text = getText(R.string.dns_configured_servers_hint_empty)
|
||||
}
|
||||
else -> {
|
||||
serversTableLayout.visibility = View.VISIBLE
|
||||
serversTableLabel.text = getString(R.string.dns_configured_servers)
|
||||
serversTableHint.text = getText(R.string.dns_configured_servers_hint)
|
||||
|
||||
serversTableLayout.removeAllViewsInLayout()
|
||||
for (i in servers.indices) {
|
||||
val server = servers[i]
|
||||
val view = inflater.inflate(R.layout.peers_configured, null)
|
||||
view.findViewById<TextView>(R.id.addressValue).text = server
|
||||
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
||||
|
||||
view.findViewById<ImageButton>(R.id.deletePeerButton).setOnClickListener { button ->
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
builder.setTitle("Remove ${server}?")
|
||||
builder.setPositiveButton(getString(R.string.remove)) { dialog, _ ->
|
||||
servers.removeAt(button.tag as Int)
|
||||
preferences.edit().apply {
|
||||
this.putString(KEY_DNS_SERVERS, servers.joinToString(","))
|
||||
this.commit()
|
||||
}
|
||||
dialog.dismiss()
|
||||
updateConfiguredServers()
|
||||
}
|
||||
builder.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
serversTableLayout.addView(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun updateUsableServers() {
|
||||
val usableTableLayout: TableLayout = findViewById(R.id.usableDnsTableLayout)
|
||||
val defaultServers = DEFAULT_DNS_SERVERS.split(",")
|
||||
|
||||
defaultServers.forEach {
|
||||
val server = it
|
||||
val view = inflater.inflate(R.layout.dns_server_usable, null)
|
||||
view.findViewById<TextView>(R.id.serverValue).text = server
|
||||
val addButton = view.findViewById<ImageButton>(R.id.addButton)
|
||||
addButton.tag = server
|
||||
|
||||
addButton.setOnClickListener { button ->
|
||||
servers.add(button.tag as String)
|
||||
preferences.edit().apply {
|
||||
this.putString(KEY_DNS_SERVERS, servers.joinToString(","))
|
||||
this.commit()
|
||||
}
|
||||
updateConfiguredServers()
|
||||
}
|
||||
view.setOnLongClickListener {
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
builder.setTitle(getString(R.string.dns_server_info_dialog_title))
|
||||
builder.setMessage(getText(R.string.dns_server_info_revertron))
|
||||
builder.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
builder.show()
|
||||
true
|
||||
}
|
||||
|
||||
usableTableLayout.addView(view)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@ class MainActivity : AppCompatActivity() {
|
|||
private lateinit var coordinatesLabel: TextView
|
||||
private lateinit var peersLabel: TextView
|
||||
private lateinit var peersRow: TableRow
|
||||
private lateinit var dnsLabel: TextView
|
||||
private lateinit var dnsRow: TableRow
|
||||
private lateinit var settingsRow: TableRow
|
||||
|
||||
private fun start() {
|
||||
|
@ -47,13 +49,15 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
findViewById<TextView>(R.id.versionValue).text = Mobile.getVersion()
|
||||
|
||||
enabledSwitch = findViewById(R.id.enableMulticastBeacon)
|
||||
enabledSwitch = findViewById(R.id.enableYggdrasil)
|
||||
enabledLabel = findViewById(R.id.yggdrasilStatusLabel)
|
||||
ipAddressLabel = findViewById(R.id.ipAddressValue)
|
||||
subnetLabel = findViewById(R.id.subnetValue)
|
||||
coordinatesLabel = findViewById(R.id.coordinatesValue)
|
||||
peersLabel = findViewById(R.id.peersValue)
|
||||
peersRow = findViewById(R.id.peersTableRow)
|
||||
dnsLabel = findViewById(R.id.dnsValue)
|
||||
dnsRow = findViewById(R.id.dnsTableRow)
|
||||
settingsRow = findViewById(R.id.settingsTableRow)
|
||||
|
||||
enabledLabel.setTextColor(Color.GRAY)
|
||||
|
@ -82,6 +86,12 @@ class MainActivity : AppCompatActivity() {
|
|||
startActivity(intent)
|
||||
}
|
||||
|
||||
dnsRow.isClickable = true
|
||||
dnsRow.setOnClickListener {
|
||||
val intent = Intent(this, DnsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
settingsRow.isClickable = true
|
||||
settingsRow.setOnClickListener {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
|
@ -94,6 +104,18 @@ class MainActivity : AppCompatActivity() {
|
|||
LocalBroadcastManager.getInstance(this).registerReceiver(
|
||||
receiver, IntentFilter(PacketTunnelState.RECEIVER_INTENT)
|
||||
)
|
||||
val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
|
||||
if (serverString!!.isNotEmpty()) {
|
||||
val servers = serverString.split(",")
|
||||
dnsLabel.text = when (servers.size) {
|
||||
0 -> "No servers"
|
||||
1 -> "1 server"
|
||||
else -> "${servers.size} servers"
|
||||
}
|
||||
} else {
|
||||
dnsLabel.text = "No servers"
|
||||
}
|
||||
}
|
||||
|
||||
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
|
|
|
@ -17,6 +17,8 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
private const val TAG = "PacketTunnelProvider"
|
||||
|
||||
class PacketTunnelProvider: VpnService() {
|
||||
companion object {
|
||||
const val RECEIVER_INTENT = "eu.neilalexander.yggdrasil.PacketTunnelProvider.MESSAGE"
|
||||
|
@ -50,16 +52,16 @@ class PacketTunnelProvider: VpnService() {
|
|||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (intent == null) {
|
||||
Log.d("PacketTunnelProvider", "Intent is null")
|
||||
Log.d(TAG, "Intent is null")
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
return when (intent.action ?: ACTION_STOP) {
|
||||
ACTION_STOP -> {
|
||||
Log.d("PacketTunnelProvider", "Stopping...")
|
||||
Log.d(TAG, "Stopping...")
|
||||
stop(); START_NOT_STICKY
|
||||
}
|
||||
else -> {
|
||||
Log.d("PacketTunnelProvider", "Starting...")
|
||||
Log.d(TAG, "Starting...")
|
||||
start(); START_STICKY
|
||||
}
|
||||
}
|
||||
|
@ -70,11 +72,11 @@ class PacketTunnelProvider: VpnService() {
|
|||
return
|
||||
}
|
||||
|
||||
Log.d("PacketTunnelProvider", config.getJSON().toString())
|
||||
Log.d(TAG, config.getJSON().toString())
|
||||
yggdrasil.startJSON(config.getJSONByteArray())
|
||||
|
||||
val address = yggdrasil.addressString
|
||||
var builder = Builder()
|
||||
val builder = Builder()
|
||||
.addAddress(address, 7)
|
||||
.addRoute("200::", 7)
|
||||
// We do this to trick the DNS-resolver into thinking that we have "regular" IPv6,
|
||||
|
@ -97,6 +99,21 @@ class PacketTunnelProvider: VpnService() {
|
|||
builder.setMetered(false)
|
||||
}
|
||||
|
||||
val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext)
|
||||
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
|
||||
if (serverString!!.isNotEmpty()) {
|
||||
val servers = serverString.split(",")
|
||||
if (servers.isNotEmpty()) {
|
||||
servers.forEach {
|
||||
Log.i(TAG, "Using DNS server $it")
|
||||
builder.addDnsServer(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preferences.getBoolean(KEY_ENABLE_CHROME_FIX, false)) {
|
||||
builder.addRoute("2001:4860:4860::8888", 128)
|
||||
}
|
||||
|
||||
parcel = builder.establish()
|
||||
val parcel = parcel
|
||||
if (parcel == null || !parcel.fileDescriptor.valid()) {
|
||||
|
@ -123,7 +140,7 @@ class PacketTunnelProvider: VpnService() {
|
|||
intent.putExtra("ip", yggdrasil.addressString)
|
||||
intent.putExtra("subnet", yggdrasil.subnetString)
|
||||
intent.putExtra("coords", yggdrasil.coordsString)
|
||||
intent.putExtra("peers", JSONArray(yggdrasil.peersJSON).length())
|
||||
intent.putExtra("peers", yggdrasil.peersJSON)
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
|
||||
}
|
||||
|
||||
|
@ -214,7 +231,7 @@ class PacketTunnelProvider: VpnService() {
|
|||
}
|
||||
|
||||
private fun reader() {
|
||||
var b = ByteArray(65535)
|
||||
val b = ByteArray(65535)
|
||||
reads@ while (started.get()) {
|
||||
val readerStream = readerStream
|
||||
val readerThread = readerThread
|
||||
|
|
|
@ -49,14 +49,14 @@ class PeersActivity : AppCompatActivity() {
|
|||
|
||||
addPeerButton = findViewById(R.id.addPeerButton)
|
||||
addPeerButton.setOnClickListener {
|
||||
var view = inflater.inflate(R.layout.dialog_addpeer, null)
|
||||
var input = view.findViewById<TextInputEditText>(R.id.addPeerInput)
|
||||
val view = inflater.inflate(R.layout.dialog_addpeer, null)
|
||||
val input = view.findViewById<TextInputEditText>(R.id.addPeerInput)
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog))
|
||||
builder.setTitle("Add Configured Peer")
|
||||
builder.setView(view)
|
||||
builder.setPositiveButton("Add") { dialog, _ ->
|
||||
config.updateJSON { json ->
|
||||
json.getJSONArray("Peers").put(input.text)
|
||||
json.getJSONArray("Peers").put(input.text.toString().trim())
|
||||
}
|
||||
dialog.dismiss()
|
||||
updateConfiguredPeers()
|
||||
|
@ -90,7 +90,7 @@ class PeersActivity : AppCompatActivity() {
|
|||
configuredTableLayout.removeAllViewsInLayout()
|
||||
for (i in 0 until peers.length()) {
|
||||
val peer = peers[i].toString()
|
||||
var view = inflater.inflate(R.layout.peers_configured, null)
|
||||
val view = inflater.inflate(R.layout.peers_configured, null)
|
||||
view.findViewById<TextView>(R.id.addressValue).text = peer
|
||||
view.findViewById<ImageButton>(R.id.deletePeerButton).tag = i
|
||||
|
||||
|
@ -130,7 +130,7 @@ class PeersActivity : AppCompatActivity() {
|
|||
connectedTableLayout.removeAllViewsInLayout()
|
||||
for (i in 0 until peers.length()) {
|
||||
val peer = peers.getJSONObject(i)
|
||||
var view = inflater.inflate(R.layout.peers_connected, null)
|
||||
val view = inflater.inflate(R.layout.peers_connected, null)
|
||||
val ip = peer.getString("IP")
|
||||
view.findViewById<TextView>(R.id.addressLabel).text = ip
|
||||
view.findViewById<TextView>(R.id.detailsLabel).text = peer.getString("Remote")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue