websocket

This commit is contained in:
wanghongenpin
2023-08-11 16:47:28 +08:00
parent c7f7a645e1
commit 47805f83e0
13 changed files with 198 additions and 32 deletions

View File

@@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace "com.network.proxy"
compileSdkVersion flutter.compileSdkVersion
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {

View File

@@ -42,7 +42,7 @@
android:name=".VpnAlertDialog"
android:exported="false">
<intent-filter>
<action android:name="ProxyVpnService" />
<action android:name="com.network.proxy.ProxyVpnService" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.

View File

@@ -1,5 +1,6 @@
package com.network.proxy
import ProxyVpnRunnable
import android.content.Intent
import android.net.ProxyInfo
import android.net.VpnService
@@ -42,22 +43,36 @@ class ProxyVpnService : VpnService() {
vpnInterface = createVpnInterface(proxyHost, proxyPort)
if (vpnInterface == null) {
val alertDialog = Intent(applicationContext, VpnAlertDialog::class.java)
.setAction("com.network.proxy.ProxyVpnService")
alertDialog.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(alertDialog)
return
}
var vpnRunnable = ProxyVpnRunnable(
vpnInterface!!,
proxyConfig.ip,
proxyConfig.port,
interceptedPorts.toIntArray()
)
}
private fun createVpnInterface(proxyHost: String, proxyPort: Int): ParcelFileDescriptor? {
return Builder()
.setMtu(1500)
.addAddress("10.0.0.2", 32)
.addRoute("0.0.0.0", 0)
.addDnsServer("114.114.114.114")
.addDnsServer("8.8.8.8")
.setSession(baseContext.applicationInfo.name)
.also {
.allowBypass()
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
it.addDisallowedApplication(baseContext.packageName)
.setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort))
addDisallowedApplication(baseContext.packageName)
setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort))
}
}
.setBlocking(true) // We use a blocking loop to read in ProxyVpnRunnable
.establish()
}

View File

@@ -0,0 +1,38 @@
package com.network.proxy;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class VpnSocketProxy {
FileChannel vpnInput;
FileChannel vpnOutput;
public VpnSocketProxy(FileDescriptor fileDescriptor) {
vpnInput = new FileInputStream(fileDescriptor).getChannel();
vpnOutput = new FileOutputStream(fileDescriptor).getChannel();
}
public void accept(byte[] buffer) throws Exception {
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);
while (true) {
int read = vpnInput.read(packet);
if (read > 0) {
packet.flip();
packet.clear();
vpnOutput.write(packet);
packet.clear();
} else {
break;
}
}
}
}

View File

@@ -0,0 +1,116 @@
import android.os.ParcelFileDescriptor
import android.util.Log
import android.util.SparseArray
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.InetSocketAddress
import java.nio.ByteBuffer
const val MAX_PACKET_LEN = 1500
const val TAG = "ProxyVpnRunnable"
class ProxyVpnRunnable(
vpnInterface: ParcelFileDescriptor,
proxyHost: String,
proxyPort: Int,
redirectPorts: IntArray
) : Runnable {
@Volatile private var running = false
private val vpnReadStream = FileInputStream(vpnInterface.fileDescriptor)
// 此VPN接收的来自上游服务器的数据包
private val vpnWriteStream = FileOutputStream(vpnInterface.fileDescriptor)
private val vpnPacketWriter = ClientPacketWriter(vpnWriteStream)
private val vpnPacketWriterThread = Thread(vpnPacketWriter)
// Background service & task for non-blocking socket
private val nioService = SocketNIODataService(vpnPacketWriter)
private val dataServiceThread = Thread(nioService, "Socket NIO thread")
private val manager = SessionManager()
private val handler = SessionHandler(manager, nioService, vpnPacketWriter)
// Allocate the buffer for a single packet.
private val packet = ByteBuffer.allocate(MAX_PACKET_LEN)
//重定向流量应该转发到代理地址
private val portRedirections = SparseArray<InetSocketAddress>().apply {
val proxyAddress = InetSocketAddress(proxyHost, proxyPort)
redirectPorts.forEach {
append(it, proxyAddress)
}
}
override fun run() {
Log.i(TAG, "Vpn thread starting")
manager.setTcpPortRedirections(portRedirections)
dataServiceThread.start()
vpnPacketWriterThread.start()
var data: ByteArray
var length: Int
running = true
while (running) {
try {
data = packet.array()
length = vpnReadStream.read(data)
if (length > 0) {
try {
packet.limit(length)
handler.handlePacket(packet)
} catch (e: Exception) {
val errorMessage = (e.message ?: e.toString())
Log.e(TAG, errorMessage)
val isIgnorable =
(e is ConnectException && errorMessage == "Permission denied") ||
// Nothing we can do if the internet goes down:
(e is ConnectException && errorMessage == "Network is unreachable") ||
(e is ConnectException && errorMessage.contains("ENETUNREACH")) ||
// Too many open files - can't make more sockets, not much we can do:
(e is ConnectException && errorMessage == "Too many open files") ||
(e is ConnectException && errorMessage.contains("EMFILE")) ||
// IPv6 is not supported here yet:
(e is PacketHeaderException && errorMessage.contains("IP version should be 4 but was 6"))
if (!isIgnorable) {
Sentry.capture(e)
}
}
packet.clear()
} else {
Thread.sleep(10)
}
} catch (e: InterruptedException) {
Log.i(TAG, "Sleep interrupted: " + e.message)
} catch (e: InterruptedIOException) {
Log.i(TAG, "Read interrupted: " + e.message)
}
}
Log.i(TAG, "Vpn thread shutting down")
}
fun stop() {
if (running) {
running = false
nioService.shutdown()
dataServiceThread.interrupt()
vpnPacketWriter.shutdown()
vpnPacketWriterThread.interrupt()
} else {
Log.w(TAG, "Vpn runnable stopped, but it's not running")
}
}
}