Reputation: 11
I'm working on a pet VPN project for Android. Now I need to develop an application that would send all packets from the device to a remote server and receive a response from it (without authorization and data encryption). The server part is ready and working fine, but there are problems on the client side. After I call VPNService.establish, a ParcelFileDescriptor is returned to me (i.e. a tun is created on the device) and the UDP connection to the server disappears. I can't send any packets to the server, although before VPNService.establish I can communicate with the server without any problems. Here is my VPNService code:
class DPNService : VpnService() {
private val currentConnection = AtomicReference<Thread>()
private val handshakeServiceListener = object : HandshakeService.Listener {
override fun onError(exception: Exception) {
Log.e(TAG, "Handshake error", exception)
}
override fun onSuccess(params: ServerParams, tunnel: DatagramChannel) {
protect(tunnel.socket())
updateNotification("VPN is connecting...")
val vpnInterface = configureVpnService(params)
vpnInterface?.let {
connect(tunnel, it)
}
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if (intent.action == ACTION_CONNECT) {
val tunnel = DatagramChannel.open()
if (protect(tunnel.socket())) {
val handshakeService = HandshakeService(tunnel, handshakeServiceListener)
handshakeService.sendHandshake()
}
return START_STICKY
}
if (intent.action == ACTION_DISCONNECT){
disconnect()
return START_NOT_STICKY
}
return super.onStartCommand(intent, flags, startId)
}
private fun connect(tunnel: DatagramChannel, vpnInterface: ParcelFileDescriptor?) {
val connection = DPNConnection(tunnel, vpnInterface)
val thread = Thread(connection, "DPN Connection")
setCurrentConnection(thread)
thread.start()
updateNotification("VPN is connected")
}
private fun configureVpnService(serverParams: ServerParams): ParcelFileDescriptor? {
val builder = this.Builder()
builder.addAddress(serverParams.address, serverParams.addressPrefixLength)
builder.addDnsServer(serverParams.dns)
builder.addRoute(serverParams.route, serverParams.routePrefixLength)
builder.setMtu(serverParams.mtu)
builder.addDisallowedApplication("com.google.chrome")
builder.addDisallowedApplication("com.werebug.androidnetcat")
builder.setSession(serverParams.address)
return builder.establish()
}
private fun disconnect() {
setCurrentConnection(null)
stopSelf()
}
override fun onDestroy() {
disconnect()
}
private fun setCurrentConnection(thread: Thread?) {
// set new thread and interrupt old
currentConnection.getAndSet(thread)?.interrupt()
}
private fun updateNotification(message: String) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val notificationChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH,
)
val notification = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_vpn_key)
.setContentText(message)
.build()
notificationManager.createNotificationChannel(notificationChannel)
notificationManager.notify(1, notification)
}
companion object {
const val TAG = "DPNService"
const val ACTION_CONNECT = "DPNService.CONNECT"
const val ACTION_DISCONNECT = "DPNService.DISCONNECT"
const val NOTIFICATION_CHANNEL_ID = "DPN"
}
}
class DPNConnection(
private val tunnel: DatagramChannel,
private val vpnInterface: ParcelFileDescriptor?,
) : Runnable {
override fun run() {
var iteration = 0
while (iteration < 100) {
iteration++
try {
tunnel.sendMessage("Hello DPN Server Side".toByteArray())
} catch (e: Exception) {
Log.e(TAG, "DPNConnection", e)
}
}
tunnel.close()
}
private fun DatagramChannel.sendMessage(byteArray: ByteArray) {
val buffer = ByteBuffer.allocate(1024)
buffer.put(byteArray)
buffer.flip()
write(buffer)
}
companion object {
private const val TAG = "DPNConnection"
}
}
And here is the exception that I get when I try to send a package:
DPNConnection
java.net.PortUnreachableException
at sun.nio.ch.DatagramDispatcher.write0(Native Method)
at sun.nio.ch.DatagramDispatcher.write(DatagramDispatcher.java:60)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.DatagramChannelImpl.write(DatagramChannelImpl.java:649)
at com.example.dpn.data.DPNConnection.sendMessage(DPNConnection.kt:35)
at com.example.dpn.data.DPNConnection.run(DPNConnection.kt:20)
at java.lang.Thread.run(Thread.java:1012)
I expected that I would see the message "Hello DPN Server Side" in the server logs. But this message does not reach the server and perhaps does not leave the device. I look in WireShark and don't see any packets after VpnService.establish is called. Moreover, if I send a packet to this port from another device, then the server side reads it
Upvotes: 1
Views: 114