diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt index 770e2d0..8daba64 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/MainActivity.kt @@ -2,10 +2,7 @@ package io.github.chronosx88.yggdrasil import android.app.Activity import android.app.ActivityManager -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent +import android.content.* import android.net.VpnService import android.os.Bundle import android.util.Log @@ -18,17 +15,26 @@ import io.github.chronosx88.yggdrasil.models.DNSInfo import io.github.chronosx88.yggdrasil.models.PeerInfo import io.github.chronosx88.yggdrasil.models.config.DNSInfoListAdapter import io.github.chronosx88.yggdrasil.models.config.PeerInfoListAdapter +import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializePeerStringList2PeerInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringList2DNSInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringList2PeerInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringSet2DNSInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringSet2PeerInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.serializeDNSInfoSet2StringList import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.serializePeerInfoSet2StringList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.net.InetAddress +import kotlin.concurrent.thread class MainActivity : AppCompatActivity() { companion object { + const val STATUS_PEERS_UPDATE = 12 + const val MESH_PEERS = "MESH_PEERS" const val STATIC_IP = "STATIC_IP_FLAG" const val signingPrivateKey = "signingPrivateKey" const val signingPublicKey = "signingPublicKey" @@ -38,6 +44,7 @@ class MainActivity : AppCompatActivity() { const val STOP = "STOP" const val START = "START" const val UPDATE_DNS = "UPDATE_DNS" + const val UPDATE_PEERS = "UPDATE_PEERS" const val PARAM_PINTENT = "pendingIntent" const val STATUS_START = 7 const val STATUS_FINISH = 8 @@ -49,7 +56,7 @@ class MainActivity : AppCompatActivity() { const val DNS_LIST_CODE = 2000 const val PEER_LIST = "PEERS_LIST" const val DNS_LIST = "DNS_LIST" - const val CURRENT_PEERS = "CURRENT_PEERS_v1.2" + const val CURRENT_PEERS = "CURRENT_PEERS_v1.2.1" const val CURRENT_DNS = "CURRENT_DNS_v1.2" const val START_VPN = "START_VPN" private const val TAG="Yggdrasil" @@ -62,6 +69,7 @@ class MainActivity : AppCompatActivity() { private var currentPeers = setOf() private var currentDNS = setOf() + private var meshPeersReceiver: BroadcastReceiver? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -169,6 +177,16 @@ class MainActivity : AppCompatActivity() { startService(intent) } + private fun updatePeers(){ + Log.d(TAG,"Update Peers") + val intent = Intent(this, YggdrasilTunService::class.java) + val TASK_CODE = 100 + val pi = createPendingResult(TASK_CODE, intent, 0) + intent.putExtra(PARAM_PINTENT, pi) + intent.putExtra(COMMAND, UPDATE_PEERS) + startService(intent) + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) @@ -244,7 +262,20 @@ class MainActivity : AppCompatActivity() { } when (resultCode) { - STATUS_START -> print("service started") + STATUS_START -> { + print("service started") + if(this.currentPeers.isEmpty()){ + //this is Mesh mode, send Peers update every 5 sec + thread(start = true) { + while(true) { + Thread.sleep(5000) + if(isStarted) { + updatePeers() + } + } + } + } + } STATUS_FINISH -> { isStarted = true val ipLayout = findViewById(R.id.ipLayout) @@ -257,6 +288,23 @@ class MainActivity : AppCompatActivity() { val ipLayout = findViewById(R.id.ipLayout) ipLayout.visibility = View.GONE } + STATUS_PEERS_UPDATE ->{ + if(data!!.extras!=null) { + thread(start = true) { + val meshPeers = deserializePeerStringList2PeerInfoSet( + data.extras!!.getStringArrayList(MESH_PEERS) + ) + val listView = findViewById(R.id.peers) + val adapter = PeerInfoListAdapter( + this@MainActivity, + meshPeers.sortedWith(compareBy { it.ping }) + ) + runOnUiThread { + listView.adapter = adapter + } + } + } + } else -> { // Note the block } @@ -290,4 +338,11 @@ class MainActivity : AppCompatActivity() { preferences.getString(STATIC_IP, null) != null } + override fun onDestroy() { + super.onDestroy() + if (meshPeersReceiver != null){ + unregisterReceiver(meshPeersReceiver); + } + } + } diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt index 33e4abc..e778082 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/YggdrasilTunService.kt @@ -16,9 +16,12 @@ import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.preference.PreferenceManager import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import dummy.ConduitEndpoint import io.github.chronosx88.yggdrasil.models.DNSInfo import io.github.chronosx88.yggdrasil.models.PeerInfo +import io.github.chronosx88.yggdrasil.models.config.Peer +import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.convertPeer2PeerStringList import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.convertPeerInfoSet2PeerIdSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringList2DNSInfoSet import io.github.chronosx88.yggdrasil.models.config.Utils.Companion.deserializeStringList2PeerInfoSet @@ -27,6 +30,7 @@ import mobile.Mobile import mobile.Yggdrasil import java.io.* import java.net.Inet6Address +import kotlin.concurrent.thread class YggdrasilTunService : VpnService() { @@ -56,10 +60,9 @@ class YggdrasilTunService : VpnService() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - + val pi: PendingIntent? = intent?.getParcelableExtra(MainActivity.PARAM_PINTENT) when(intent?.getStringExtra(MainActivity.COMMAND)){ MainActivity.STOP ->{ - val pi: PendingIntent? = intent.getParcelableExtra(MainActivity.PARAM_PINTENT) stopVpn(pi) startForeground(FOREGROUND_ID, foregroundNotification("Yggdrasil service stopped")) } @@ -67,7 +70,6 @@ class YggdrasilTunService : VpnService() { val peers = deserializeStringList2PeerInfoSet(intent.getStringArrayListExtra(MainActivity.PEERS)) val dns = deserializeStringList2DNSInfoSet(intent.getStringArrayListExtra(MainActivity.DNS)) val staticIP: Boolean = intent.getBooleanExtra(MainActivity.STATIC_IP, false) - val pi: PendingIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT) ygg = Yggdrasil() setupTunInterface(pi, peers, dns, staticIP) startForeground(FOREGROUND_ID, foregroundNotification("Yggdrasil service started")) @@ -76,6 +78,9 @@ class YggdrasilTunService : VpnService() { val dns = deserializeStringList2DNSInfoSet(intent.getStringArrayListExtra(MainActivity.DNS)) setupIOStreams(dns) } + MainActivity.UPDATE_PEERS ->{ + sendMeshPeerStatus(pi) + } } return START_NOT_STICKY @@ -141,11 +146,22 @@ class YggdrasilTunService : VpnService() { writePacketsToTun(yggConduitEndpoint) } } - val intent: Intent = Intent().putExtra(MainActivity.IPv6, address) pi.send(this, MainActivity.STATUS_FINISH, intent) } + private fun sendMeshPeerStatus(pi: PendingIntent?){ + class Token : TypeToken>() + var add = ygg.addressString + var meshPeers: List = gson.fromJson(ygg.peersJSON, Token().type) + val intent: Intent = Intent().putStringArrayListExtra( + MainActivity.MESH_PEERS, + convertPeer2PeerStringList(meshPeers) + ); + pi?.send(this, MainActivity.STATUS_PEERS_UPDATE, intent) + + } + private fun fixConfig( config: MutableMap, peers: Set, @@ -231,7 +247,6 @@ class YggdrasilTunService : VpnService() { private fun stopVpn(pi: PendingIntent?) { isClosed = true; - tunInputStream!!.close() tunOutputStream!!.close() tunInterface!!.close() diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt index 56285bc..a1a1d4b 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/PeerInfo.kt @@ -19,12 +19,28 @@ class PeerInfo { this.port = port this.countryCode = countryCode } + + constructor(schema: String, address: InetAddress, port: Int, countryCode: String?, isMeshPeer: Boolean){ + this.schema = schema + this.address = address + var a = address.toString(); + if(a.lastIndexOf('/')>0){ + this.hostName = a.split("/")[0] + } else { + this.hostName = a.substring(1) + } + this.port = port + this.countryCode = countryCode + this.isMeshPeer = isMeshPeer + } + var schema: String var address: InetAddress var hostName: String var port = 0 - var countryCode: String + var countryCode: String?=null var ping: Int = Int.MAX_VALUE + var isMeshPeer = false override fun toString(): String { if(this.hostName.contains(":")) { @@ -39,7 +55,14 @@ class PeerInfo { } fun getCountry(context: Context): CCPCountry? { - return CCPCountry.getCountryForNameCodeFromLibraryMasterList(context, CountryCodePicker.Language.ENGLISH, countryCode) + if(countryCode==null){ + return null + } else { + return CCPCountry.getCountryForNameCodeFromLibraryMasterList( + context, + CountryCodePicker.Language.ENGLISH, + countryCode + ) + } } - } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Peer.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Peer.kt new file mode 100644 index 0000000..dd6a0ef --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Peer.kt @@ -0,0 +1,13 @@ +package io.github.chronosx88.yggdrasil.models.config + +import com.google.gson.annotations.SerializedName + +data class Peer ( + //Example [{"PublicKey":[154,201,118,156,19,74,134,115,94,159,76,86,36,192,221,105,220,254,226,161,108,226,17,192,75,243,225,15,42,195,155,2],"Endpoint":"(self)","BytesSent":0,"BytesRecvd":0,"Protocol":"self","Port":0,"Uptime":209900460}] + @SerializedName("Endpoint") var endpoint : String, + @SerializedName("Port") var port : Int, + @SerializedName("Uptime") var uptime : Long, + @SerializedName("Protocol") var protocol : String, + @SerializedName("BytesSent") var bytesSent : Long, + @SerializedName("BytesRecvd") var bytesReceived : Long +) \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt index 2c549e2..f777eeb 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/PeerInfoListAdapter.kt @@ -31,7 +31,11 @@ class PeerInfoListAdapter( peerInfoHolder = listItem.tag as PeerInfoHolder } val currentPeer = allPeers[position] - peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID) + if(currentPeer.isMeshPeer){ + //TODO set mesh icon + } else { + peerInfoHolder.countryFlag.setImageResource(currentPeer.getCountry(mContext)!!.flagID) + } peerInfoHolder.peerInfoText.text = currentPeer.toString() return listItem!! } diff --git a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Utils.kt b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Utils.kt index 470132f..f65a557 100644 --- a/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Utils.kt +++ b/app/src/main/java/io/github/chronosx88/yggdrasil/models/config/Utils.kt @@ -99,5 +99,39 @@ class Utils { return out } + @JvmStatic + fun convertPeer2PeerStringList(list: List): ArrayList { + var out = ArrayList() + var gson = Gson() + for(p in list) { + out.add(gson.toJson(p)) + } + return out + } + + @JvmStatic + fun deserializePeerStringList2PeerInfoSet(list: List?): MutableSet { + var gson = Gson() + var out = mutableSetOf() + if (list != null) { + for(s in list) { + var p = gson.fromJson(s, Peer::class.java) + if(p.endpoint == "(self)"){ + out.add(PeerInfo(p.protocol, InetAddress.getByName("localhost"), p.port, null, true)) + } else { + out.add( + PeerInfo( + p.protocol, + InetAddress.getByName(p.endpoint), + p.port, + null, + true + ) + ) + } + } + } + return out + } } } \ No newline at end of file