Android Kotlin SSH VPN

问题描述 投票:0回答:1

嘿伙计们,我尝试制作简单的 android ssh vpn 我是 android 编程的新手,并制作了简单的逻辑来工作,代码中的所有内容看起来都不错,但不知何故,当我运行代码时,我有无效参数错误,不知道问题是什么 这是代码

VPN服务逻辑


import android.content.Intent
import android.net.VpnService
import android.os.ParcelFileDescriptor
import android.util.Log
import com.jcraft.jsch.ChannelDirectTCPIP
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.Inet4Address
import java.net.InetSocketAddress
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.channels.DatagramChannel

class ServiceAction: VpnService() {
    private val tag = "serviceAction"

    private var vpnInterface: ParcelFileDescriptor? = null
    private var sshClient: SshLogic? = null
    private var directTcpIpChannel: ChannelDirectTCPIP? = null
    private var isConnected: Boolean? = false

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.getStringExtra("COMMAND") == "STOP"){
            stopVPN()
        }
        Log.d(tag, "Activity Extras -> ${intent?.extras.toString()}")
        return START_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        stopSelf()
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(tag, "Creating Service Action")
        //Initialize The VPN Interface
        setupVPN()
        //Setup the ssh Client
        initializeSSHClient()
        //start The VPN
        startVPN()

    }

    @OptIn(DelicateCoroutinesApi::class)
    private fun startVPN(){
        GlobalScope.launch(Dispatchers.IO) {
            runVpn()
        }
    }

    @OptIn(DelicateCoroutinesApi::class)
    private fun initializeSSHClient(){
        GlobalScope.launch(Dispatchers.IO) {
            sshClient = SshLogic("server hostname", port, "username", "password")
            isConnected = sshClient?.connect()
            directTcpIpChannel = sshClient!!.openChannel()
        }
    }

    private fun stopVPN(){
        sshClient?.disconnect()
        vpnInterface?.close()
        stopSelf()
        Log.d(tag, "Stop Vpn!")
    }

    private fun setupVPN(){
        val packageName = applicationContext.packageName
        val builder = Builder()
            .addAddress("79.76.0.1", 24)
            .addDnsServer("9.9.9.9")
            .addRoute("0.0.0.0", 0)
            .setSession(tag)
            .addDisallowedApplication(packageName)
            .setMtu(1500)
        vpnInterface = builder.establish()
        Log.d(tag, "VPN Interface Established")
    }

    @OptIn(DelicateCoroutinesApi::class)
    fun runVpn() {

        Thread.sleep(3000L)
        Log.d(tag, "Running VPN Loop...")
        val vpnInterfaceChannelIn = FileInputStream(vpnInterface?.fileDescriptor).channel
        val vpnInterfaceChannelOut = FileOutputStream(vpnInterface?.fileDescriptor).channel
        Log.d(tag, "Vpn Interface state ${vpnInterface?.fileDescriptor?.valid()}")

        //Log.d(tag, "VPN  Interface File -> ${vpnInterface?.fileDescriptor}")

        directTcpIpChannel?.let {
            val channelInput = it.inputStream
            val channelOutput = it.outputStream
            it.connect()
            Log.d(tag, "VPN Loop Started with channel id of ${it.id}")
            if (it.isConnected){

                GlobalScope.launch(Dispatchers.IO) {

                    loop@ while (true){
                        val buffer = ByteBuffer.allocate(1500)
                        buffer.clear()
                        val readBytes = vpnInterfaceChannelIn.read(buffer)
                        if (readBytes <= 0){
                            continue@loop
                        }
                        Log.d(tag, "Incoming Traffic w/ Length $readBytes")
                        buffer.flip()
                        channelOutput.write(buffer.array())
                        channelOutput.flush()

                        buffer.clear()
                        val channelReadBytes = channelInput.read(buffer.array())
                        if (channelReadBytes <= 0){
                            continue@loop
                        }
                        Log.d(tag, "Length From Server $channelReadBytes")
                        //There is a Bug here `out of the band index` fix it
                        try {
                            Log.d(tag, "Length of prepared data to write in to interface ${buffer.get()}")
                            vpnInterfaceChannelOut.write(buffer)
                        } catch (e: Exception){
                            e.printStackTrace()
                            //stopVPN()
                        }

                    }
                }

            }else{
                Log.d(tag, "Channel is not Connected!")
                stopVPN()
            }
        }

        //channelIn.close()
        //channelOut.close()
        //stopVPN()
    }
}

ssh 连接逻辑


import android.util.Log
import com.jcraft.jsch.ChannelDirectTCPIP
import com.jcraft.jsch.JSch
import com.jcraft.jsch.JSchException
import com.jcraft.jsch.Session

class SshLogic(
    private val hostname : String,
    private val port : Int,
    private val username : String,
    private val password : String
) {
    private lateinit var session: Session
    private val tag = "sshLogic"
    fun connect() : Boolean {
        Log.d(tag, "Trying To Connect")
        val jsch = JSch()
        session = jsch.getSession(username, hostname, port)
        session.setPassword(password)
        session.setConfig("StrictHostKeyChecking", "no")

        try {
            session.connect()
            Log.d(tag, "Connected to ssh server")
            Log.d(tag, "User Info -> ${session.userInfo} - Status : ${session.isConnected}")
            return true
        } catch (e: Exception) {
            e.printStackTrace()
        }
        Log.d(tag, "Failed To Connect")
        return false
    }

    fun disconnect() {
        Log.d(tag, "Disconnecting")
        session.disconnect()
    }

    fun openChannel(): ChannelDirectTCPIP? {
        Log.d(tag, "Trying To Open Channel")
        var channel: ChannelDirectTCPIP? = null
        try {
            channel = session.openChannel("direct-tcpip") as ChannelDirectTCPIP
            Log.d(tag, "Channel Opened Successfully -> ${channel.id}")
        } catch (e: JSchException){
            Log.d(tag, "Error Opening Channel")
            e.printStackTrace()
        }
        channel?.setHost(hostname)
        channel?.setPort(port)
        return channel
    }
}

我希望捕获所有流量并将它们通过隧道传输到 ssh 服务器

android kotlin ssh vpn android-vpn-service
1个回答
0
投票

有一些小事情我想你需要检查一下。 然而这似乎没有任何语法或逻辑问题。

所以,我相信这需要再次检查完整的项目文件。如果可能的话,请使用上传服务或其他方式给我一份副本。

  1. 渠道使用不正确:

在 runVpn() 函数中,有一部分使用 ByteBuffer.get() 来获取准备写入接口的数据的长度。这是不正确的,它会导致“带外索引”问题。您应该使用 buffer.remaining() 来获取缓冲区中剩余的字节数。

替换此行:

Log.d(tag, "Length of prepared data to write in to interface ${buffer.get()}")

用这行:

Log.d(tag, "Length of prepared data to write in to interface ${buffer.remaining()}")
  1. 无效的 VpnInterfaceChannelOut 用法:

在 runVpn() 函数中,您使用 vpnInterfaceChannelOut.write(buffer.array()) 将缓冲区数据写入 vpnInterfaceChannelOut。这可能会导致问题,因为数组包含整个缓冲区容量,而不仅仅是实际数据。相反,您应该在写入之前使用 buffer.flip() 以确保只写入有效数据。

替换此行:

channelOutput.write(buffer.array())

用这些行:

buffer.flip()
val bufferData = ByteArray(buffer.remaining())
buffer.get(bufferData)
channelOutput.write(ByteBuffer.wrap(bufferData))
  1. 非阻塞循环:

在 runVpn() 函数中,似乎有一个 while 循环不处理非阻塞行为,并可能导致 CPU 使用率较高。为了解决这个问题,请考虑在循环中引入睡眠持续时间,让 CPU 得到一些休息。您可能还想引入一种更复杂的非阻塞方法。

  1. 确保缓冲区同步:

跨不同线程使用缓冲区时,需要确保正确的同步以避免数据损坏。考虑使用线程安全机制,如 ByteBuffer.allocateDirect() 并将其包装在 ByteBuffer 实例中,该实例可以在线程之间安全地传递。

请记住,调试这样复杂的网络相关代码可能具有挑战性,并且可能需要多次测试和迭代来识别和修复问题。此外,VPN 隧道的质量可能取决于多种因素,例如 SSH 服务器的稳定性、网络状况等。

希望你能解决这个问题,因为我也在寻找一些关于 ssh 连接用作 VPN 的示例。

© www.soinside.com 2019 - 2024. All rights reserved.