kullandığın paketi ve vpn bağlantısı yaptığın kodların bir kısmını paylaşırmısın
çünkü direkt olarak VPN bağlantısını kesmek mümkün değildir, kullanıcı tarafından yapılması gereklidir.
Şöyle tunnelservice.kt dosyam
package com.sail_tunnel.sail
import android.app.Service
import android.content.Intent
import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import com.sail_tunnel.sail.net.DefaultNetworkListener
import com.sail_tunnel.sail.net.DnsResolverCompat
import com.sail_tunnel.sail.services.VpnState
import com.sail_tunnel.sail.services.LocalDnsWorker
import com.sail_tunnel.sail.services.TunnelInstance
import com.sail_tunnel.sail.utils.readableMessage
import kotlinx.coroutines.*
import java.io.File
import java.io.IOException
class TunnelService : VpnService() {
companion object {
private const val CORE_NAME = "leaf"
private const val VPN_MTU = 1500
private const val PRIVATE_VLAN4_CLIENT = "194.36.88.50"
private const val PRIVATE_VLAN4_ROUTER = "1.1.1.1"
}
inner class NullConnectionException : NullPointerException() {
override fun getLocalizedMessage() =
"Failed to start VPN service. You might need to reboot your device."
}
private val data = VpnState.Data()
private fun startRunner() {
if (Build.VERSION.SDK_INT >= 26) startForegroundService(Intent(this, javaClass))
else startService(Intent(this, javaClass))
}
private var pfd: ParcelFileDescriptor? = null
private var active = false
private var metered = false
@Volatile
private var underlyingNetwork: Network? = null
set(value) {
field = value
if (active) setUnderlyingNetworks(underlyingNetworks)
}
private val underlyingNetworks
get() =
// clearing underlyingNetworks makes Android 9 consider the network to be metered
if (Build.VERSION.SDK_INT == 28 && metered) null else underlyingNetwork?.let {
arrayOf(
it
)
}
override fun onRevoke() = stopRunner()
private fun killProcesses(scope: CoroutineScope) {
data.proxy?.run {
shutdown(scope)
data.proxy = null
}
data.localDns?.shutdown(scope)
data.localDns = null
active = false
scope.launch { DefaultNetworkListener.stop(this) }
pfd?.close()
pfd = null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = data
if (data.state != VpnState.State.Stopped) return Service.START_NOT_STICKY
try {
data.proxy = TunnelInstance()
} catch (e: IllegalArgumentException) {
stopRunner(false, e.message)
return Service.START_NOT_STICKY
}
data.changeState(VpnState.State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
try {
preInit()
startVpn()
data.changeState(VpnState.State.Connected)
} catch (_: CancellationException) {
// if the job was cancelled, it is canceller's responsibility to call stopRunner
} catch (exc: Throwable) {
stopRunner(false, exc.readableMessage)
} finally {
data.connectingJob = null
}
}
return START_NOT_STICKY
}
private suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
private suspend fun rawResolver(query: ByteArray) =
// no need to listen for network here as this is only used for forwarding local DNS queries.
// retries should be attempted by client.
DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query)
private suspend fun startVpn() {
val builder = Builder()
.setConfigureIntent(Core.configureIntent(this))
.setSession(CORE_NAME)
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN4_CLIENT, 24)
.addDnsServer(PRIVATE_VLAN4_ROUTER)
.addRoute("0.0.0.0", 0)
active = true // possible race condition here?
builder.setUnderlyingNetworks(underlyingNetworks)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
this.pfd = builder.establish() ?: throw NullConnectionException()
val context = Core.deviceStorage
val configRoot = context.noBackupFilesDir
data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
val configFile = File(configRoot, VpnState.CONFIG_FILE)
val configContent = configFile
.readText()
.replace("{{leafLogFile}}", File(configRoot, VpnState.LEAF_LOG_FILE).absolutePath)
.replace("{{tunFd}}", this.pfd?.fd?.toLong().toString())
configFile.writeText(configContent)
data.proxy!!.start(
this,
File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
configFile,
)
}
private fun stopRunner(restart: Boolean = false, msg: String? = null) {
if (data.state == VpnState.State.Stopping) return
// channge the state
data.changeState(VpnState.State.Stopping)
GlobalScope.launch(Dispatchers.Main.immediate) {
data.connectingJob?.cancelAndJoin() // ensure stop connecting first
// we use a coroutineScope here to allow clean-up in parallel
coroutineScope {
killProcesses(GlobalScope)
}
// change the state
data.changeState(VpnState.State.Stopped, msg)
// stop the service if nothing has bound to it
if (restart) startRunner() else {
stopSelf()
}
}
}
}
bu da app model kodum. Buton işlevi
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:sail/adapters/leaf_ffi/config.dart';
import 'package:sail/channels/vpn_manager.dart';
import 'package:sail/constant/app_colors.dart';
import 'package:sail/constant/app_strings.dart';
import 'package:sail/models/base_model.dart';
import 'package:sail/models/server_model.dart';
import 'package:sail/models/user_model.dart';
import 'package:sail/utils/common_util.dart';
class AppModel extends BaseModel {
VpnManager vpnManager = VpnManager();
VpnStatus vpnStatus = VpnStatus.disconnected;
bool isOn = false;
DateTime? connectedDate;
PageController pageController = PageController(initialPage: 0);
String appTitle = AppStrings.appName;
Config config = Config();
ThemeData themeData = ThemeData(
primarySwatch: AppColors.themeColor,
visualDensity: VisualDensity.adaptivePlatformDensity,
);
AppModel() {
General general = General(
loglevel: 'info',
logoutput: '{{leafLogFile}}',
dnsServer: ['223.5.5.5', '114.114.114.114'],
tunFd: '{{tunFd}}',
routingDomainResolve: true);
List<Rule> rules = [];
// rules.add(Rule(typeField: 'EXTERNAL', target: 'Direct', filter: 'site:cn'));
rules.add(Rule(typeField: 'FINAL', target: 'Direct'));
config.general = general;
config.rules = rules;
}
final Map _tabMap = {
0: AppStrings.appName,
1: 'Paketler',
2: 'Sunucular',
3: 'Hesap Ayarları',
};
void jumpToPage(int page) {
pageController.jumpToPage(page);
appTitle = _tabMap[page];
notifyListeners();
}
void getStatus() async {
vpnStatus = await vpnManager.getStatus();
if (vpnStatus == VpnStatus.connected) {
isOn = true;
notifyListeners();
} else if (vpnStatus == VpnStatus.disconnected) {
isOn = false;
notifyListeners();
}
}
void togglePowerButton() async {
if (vpnStatus == VpnStatus.connecting) {
Fluttertoast.showToast(
msg: "Bağlanıyor, lütfen bekleyin...",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
textColor: Colors.white,
fontSize: 14.0);
return;
}
if (vpnStatus == VpnStatus.disconnecting) {
Fluttertoast.showToast(
msg: "Bağlantı kesiliyor, lütfen bekleyin...",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
textColor: Colors.white,
fontSize: 14.0);
return;
}
if (vpnStatus == VpnStatus.connected) {
vpnStatus = VpnStatus.disconnecting;
}
if (vpnStatus == VpnStatus.disconnected) {
vpnStatus = VpnStatus.connecting;
}
await vpnManager.toggle();
notifyListeners();
}
void getTunnelLog() async {
var log = await vpnManager.getTunnelLog();
print("log: $log");
}
void getTunnelConfiguration() async {
var conf = await vpnManager.getTunnelConfiguration();
print("config: $conf");
}
void setConfigProxies(UserModel userModel, ServerModel serverModel) async {
List<Proxy> proxies = [];
List<ProxyGroup> proxyGroups = [];
List<String> actors = [];
for (var server in serverModel.serverEntityList) {
Proxy proxy = Proxy(
tag: server.name,
protocol: server.type,
address: server.host,
port: server.port,
encryptMethod: server.cipher,
password: userModel.userEntity!.uuid);
proxies.add(proxy);
actors.add(server.name);
}
if (actors.isNotEmpty) {
proxyGroups.add(ProxyGroup(
tag: "UrlTest",
protocol: 'url-test',
actors: actors,
checkInterval: 600));
config.rules?.last.target = "UrlTest";
}
config.proxies = proxies;
config.proxyGroups = proxyGroups;
print("-----------------config-----------------");
print(config);
print("-----------------config-----------------");
vpnManager.setTunnelConfiguration(config.toString());
}
void setConfigRule(String tag) async {
// var proxy = config.proxies?.where((proxies) => proxies.tag == tag);
//
// if (proxy == null || proxy.isEmpty) {
// return;
// }
//
// config.rules?.last.target = tag;
//
// print("-----------------config-----------------");
// print(config);
// print("-----------------config-----------------");
//
// vpnManager.setTunnelConfiguration(config.toString());
}
}