Hey Guys I tried to make simple android ssh vpn i'm new to android programming and made simple logic to just work and everything in that code seems ok but somehow when i run the code i have Invalid Argument Error and idk what's the problem heres's the code
vpn service logic
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 connection logic
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
}
}
i was expecting to capture all the traffic and tunnel them to the ssh server
There are some minor things I guess that you need to checkup. However this doesn't seem to have any syntax or logic trouble.
So, I believe this needs the full project files to be checked again. If thats possible please hand me a copy of them using an upload service or sth.
In your runVpn() function, there is a part where you're using ByteBuffer.get() to get the length of the prepared data to write into the interface. This is incorrect, and it's causing the "out of the band index" issue. You should use buffer.remaining() instead to get the number of bytes remaining in the buffer.
Replace this line:
With this line:
In your runVpn() function, you are using vpnInterfaceChannelOut.write(buffer.array()) to write the buffer data to vpnInterfaceChannelOut. This might cause issues because the array contains the entire buffer capacity, not just the actual data. Instead, you should use buffer.flip() before writing to ensure that only the valid data is written.
Replace this line:
With these lines:
In your runVpn() function, it seems you have a while loop that doesn't handle non-blocking behavior and could lead to high CPU usage. To address this, consider introducing a sleep duration within the loop to give the CPU some rest. You might also want to introduce a more sophisticated non-blocking approach.
When using buffers across different threads, you need to ensure proper synchronization to avoid data corruption. Consider using thread-safe mechanisms like ByteBuffer.allocateDirect() and wrapping it in a ByteBuffer instance, which can be safely passed between threads.
Please keep in mind that debugging complex network-related code like this can be challenging, and it might involve testing and iterating multiple times to identify and fix issues. Also, the quality of the VPN tunnel might depend on various factors like the stability of the SSH server, network conditions, and more.
Hope you'll fix this cause I was also looking for some samples about ssh connection to be used as a vpn.