mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-20 16:15:47 +08:00
websocket
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user