Skip to content

Commit

Permalink
扫码连接终端
Browse files Browse the repository at this point in the history
  • Loading branch information
wanghongenpin committed Jul 8, 2023
1 parent 75534d9 commit 3a67c43
Show file tree
Hide file tree
Showing 28 changed files with 720 additions and 154 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## 免费开源Http、Https抓包工具,支持Windows、Mac、Android、IOS, 全平台系统
支持手机扫码连接,不用手动配置Wifi代理,包括配置同步。所有终端都可以互相扫码连接转发流量。

**Mac首次打开会提示已损坏,需要到系统偏好设置-安全性与隐私-允许任何来源。**

Expand All @@ -10,10 +11,8 @@

- [ ] 接下来会完善功能体验,JSON格式化展示,URL解码,UI优化。
- [ ] IOS上架app Store。
- [ ] 后面桌面端和手机端整合,扫码连接啥的,不用手动配置Wifi代理,包括配置同步。
- [ ] 支持安卓微信小程序抓包,安卓分为系统证书和用户证书,下载的自签名根证书安装都是用户证书,微信不信任用户证书,不Root导致Https抓不了了, 目前市场上所有抓包软件抓不了微信的包,后面单独做个运行空间插件,动态反编译修改配置,信任用户证书来解决。


<img alt="image" width="600px" height="400px" src="https://github.com/wanghongenpin/network-proxy-flutter/assets/24794200/67a2feb1-f1c3-4c0c-8737-5abe62c34794">. <img alt="image" height="500px" src="https://github.com/wanghongenpin/network_proxy_flutter/assets/24794200/1bb4b1ec-ec5c-44a7-add7-f0f94c8765b9">


5 changes: 2 additions & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,16 @@ android {

defaultConfig {
applicationId "com.network.proxy"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 19
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
multiDexEnabled true
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/>

<application
android:label="ProxyPin"
Expand Down
16 changes: 12 additions & 4 deletions android/app/src/main/kotlin/com/network/proxy/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import io.flutter.plugin.common.MethodChannel


class MainActivity : FlutterActivity() {
private val CHANNEL = "com.proxy/proxyVpn"

private val VPN_REQUEST_CODE: Int = 24
companion object {
const val VPN_CHANNEL = "com.proxy/proxyVpn"
const val VPN_REQUEST_CODE: Int = 24
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -21,7 +22,14 @@ class MainActivity : FlutterActivity() {

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
vpnMethodChannel(flutterEngine)
}

/**
* vpn方法通道
*/
private fun vpnMethodChannel(flutterEngine: FlutterEngine) {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, VPN_CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"startVpn" -> {
Expand Down
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
1 change: 0 additions & 1 deletion ios/ProxyPin/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
exit(EXIT_FAILURE)
}
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
networkSettings.mtu = 1500
NSLog(conf.debugDescription)
//http代理
let host = conf["proxyHost"] as! String
Expand Down
5 changes: 1 addition & 4 deletions ios/ProxyPin/ProxyPin.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
<array>
<string>allow-vpn</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>

</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSCameraUsageDescription</key>
<string>扫描二维码</string>
</dict>
</plist>
3 changes: 2 additions & 1 deletion ios/Runner/VpnManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ extension VpnManager{
}
}

func disconnect(){
func disconnect() {
if (activeVPN != nil) {
print("stopVPNTunnel activeVPN")
activeVPN?.connection.stopVPNTunnel()
activeVPN = nil
return
}

Expand Down
2 changes: 1 addition & 1 deletion lib/network/bin/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ProxyServer {
EventListener? listener;
RequestRewrites requestRewrites = RequestRewrites();

List<Function> _initializedListeners = [];
final List<Function> _initializedListeners = [];

ProxyServer({this.listener});

Expand Down
46 changes: 35 additions & 11 deletions lib/network/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ class Channel {
final int remotePort;

Channel(this._socket)
: _id = DateTime
.now()
.millisecondsSinceEpoch + Random().nextInt(999999),
: _id = DateTime.now().millisecondsSinceEpoch + Random().nextInt(999999),
remoteAddress = _socket.remoteAddress,
remotePort = _socket.remotePort;

Expand All @@ -56,6 +54,10 @@ class Channel {
set secureSocket(SecureSocket secureSocket) => _socket = secureSocket;

Future<void> write(Object obj) async {
if (isClosed) {
return;
}

var data = pipeline._encoder.encode(obj);
_socket.add(data);
await _socket.flush();
Expand Down Expand Up @@ -115,9 +117,23 @@ class ChannelPipeline extends ChannelHandler<Uint8List> {
_handler.channelActive(channel);
}

/// 转发请求
void relay(Channel clientChannel, Channel remoteChannel) {
var rawCodec = RawCodec();
clientChannel.pipeline.handle(rawCodec, rawCodec, RelayHandler(remoteChannel));
remoteChannel.pipeline.handle(rawCodec, rawCodec, RelayHandler(clientChannel));
}

@override
void channelRead(Channel channel, Uint8List msg) {
try {
HostAndPort? remote = channel.getAttribute(AttributeKeys.remote);
if (remote != null && channel.getAttribute(channel.id) != null) {
relay(channel, channel.getAttribute(channel.id));
_handler.channelRead(channel, msg);
return;
}

var data = _decoder.decode(msg);
if (data == null) {
return;
Expand Down Expand Up @@ -199,11 +215,11 @@ class HostAndPort {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is HostAndPort &&
runtimeType == other.runtimeType &&
scheme == other.scheme &&
host == other.host &&
port == other.port;
other is HostAndPort &&
runtimeType == other.runtimeType &&
scheme == other.scheme &&
host == other.host &&
port == other.port;

@override
int get hashCode => scheme.hashCode ^ host.hashCode ^ port.hashCode;
Expand Down Expand Up @@ -250,6 +266,7 @@ abstract interface class ChannelInitializer {
class Network {
late Function _channelInitializer;
bool enableSsl = false;
String? remoteHost;

Network initChannel(void Function(Channel channel) initializer) {
_channelInitializer = initializer;
Expand All @@ -268,6 +285,11 @@ class Network {

_onEvent(Uint8List data, Channel channel) async {
HostAndPort? hostAndPort = channel.getAttribute(AttributeKeys.host);

if (remoteHost != null) {
channel.putAttribute(AttributeKeys.remote, HostAndPort.of(remoteHost!));
}

//黑名单 直接转发
if (HostFilter.filter(hostAndPort?.host) || (hostAndPort?.isSsl() == true && !enableSsl)) {
relay(channel, channel.getAttribute(channel.id));
Expand All @@ -288,8 +310,9 @@ class Network {
try {
//客户端ssl
Channel remoteChannel = channel.getAttribute(channel.id);

remoteChannel.secureSocket =
await SecureSocket.secure(remoteChannel.socket, onBadCertificate: (certificate) => true);
await SecureSocket.secure(remoteChannel.socket, onBadCertificate: (certificate) => true);
remoteChannel.pipeline.listen(remoteChannel);

//服务端ssl
Expand Down Expand Up @@ -326,13 +349,14 @@ class Server extends Network {
Future<ServerSocket> stop() async {
if (!isRunning) return serverSocket;
isRunning = false;
return serverSocket.close();
await serverSocket.close();
return serverSocket;
}
}

class Client extends Network {
Future<Channel> connect(HostAndPort hostAndPort) async {
return Socket.connect(hostAndPort.host, hostAndPort.port, timeout: const Duration(seconds: 5))
return Socket.connect(hostAndPort.host, hostAndPort.port, timeout: const Duration(seconds: 3))
.then((socket) => listen(socket));
}
}
82 changes: 71 additions & 11 deletions lib/network/handler.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/http/http_headers.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/util/file_read.dart';
import 'package:network_proxy/network/util/host_filter.dart';
import 'package:network_proxy/network/util/request_rewrite.dart';
import 'package:network_proxy/utils/ip.dart';

import 'channel.dart';
import 'http/codec.dart';
Expand Down Expand Up @@ -35,6 +38,33 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {

@override
void channelRead(Channel channel, HttpRequest msg) async {
channel.putAttribute(AttributeKeys.request, msg);

if (msg.path == '/config' && (await localIp()) == msg.hostAndPort?.host) {
var response = HttpResponse(msg.protocolVersion, HttpStatus.ok);
var body = {
"requestRewrites": requestRewrites?.toJson(),
'whitelist': HostFilter.whitelist.toJson(),
'blacklist': HostFilter.blacklist.toJson(),
};
response.body = utf8.encode(json.encode(body));
channel.writeAndClose(response);
return;
}
if ((await localIp()) == msg.hostAndPort?.host) {
var response = HttpResponse(msg.protocolVersion, HttpStatus.ok);
response.body = utf8.encode('pong');
response.headers.set("os", Platform.operatingSystem);
response.headers.set("hostname", Platform.isAndroid ? Platform.operatingSystem : Platform.localHostname);
channel.writeAndClose(response);
return;
}

if (msg.uri == 'http://proxy.pin/ssl' || msg.requestUrl == 'http://127.0.0.1:${channel.socket.port}/ssl') {
_crtDownload(channel, msg);
return;
}

forward(channel, msg).catchError((error, trace) {
channel.close();
if (error is SocketException &&
Expand All @@ -54,19 +84,11 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {

/// 转发请求
Future<void> forward(Channel channel, HttpRequest httpRequest) async {
channel.putAttribute(AttributeKeys.request, httpRequest);

if (httpRequest.uri == 'http://proxy.pin/ssl' ||
httpRequest.requestUrl == 'http://127.0.0.1:${channel.socket.port}/ssl') {
_crtDownload(channel, httpRequest);
return;
}

var remoteChannel = await _getRemoteChannel(channel, httpRequest);

//实现抓包代理转发
if (httpRequest.method != HttpMethod.connect) {
// log.i("[${channel.id}] ${httpRequest.requestUrl}");
log.i("[${channel.id}] ${httpRequest.requestUrl}");

var replaceBody = requestRewrites?.findRequestReplaceWith(httpRequest.path);
if (replaceBody?.isNotEmpty == true) {
Expand Down Expand Up @@ -110,6 +132,15 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
clientChannel.putAttribute(AttributeKeys.host, hostAndPort);

var proxyHandler = HttpResponseProxyHandler(clientChannel, listener: listener, requestRewrites: requestRewrites);

HostAndPort? remote = clientChannel.getAttribute(AttributeKeys.remote);
if (remote != null) {
var proxyChannel = await HttpClients.connect(remote, proxyHandler);
clientChannel.putAttribute(clientId, proxyChannel);
proxyChannel.write(httpRequest);
return proxyChannel;
}

var proxyChannel = await HttpClients.connect(hostAndPort, proxyHandler);
clientChannel.putAttribute(clientId, proxyChannel);

Expand All @@ -134,7 +165,7 @@ class HttpResponseProxyHandler extends ChannelHandler<HttpResponse> {
@override
void channelRead(Channel channel, HttpResponse msg) {
msg.request = clientChannel.getAttribute(AttributeKeys.request);
msg.request?.response= msg;
msg.request?.response = msg;
// log.i("[${clientChannel.id}] Response ${msg.bodyAsString}");

var replaceBody = requestRewrites?.findResponseReplaceWith(msg.request?.path);
Expand Down Expand Up @@ -172,10 +203,39 @@ class RelayHandler extends ChannelHandler<Object> {

class HttpClients {
/// 建立连接
static Future<Channel> connect(HostAndPort hostAndPort, ChannelHandler<HttpResponse> handler) async {
static Future<Channel> connect(HostAndPort hostAndPort, ChannelHandler handler) async {
var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), handler));

return client.connect(hostAndPort);
}

static Future<HttpResponse> get(String url, {Duration duration = const Duration(seconds: 3)}) async {
var httpResponseHandler = HttpResponseHandler();

var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), httpResponseHandler));

Channel channel = await client.connect(HostAndPort.of(url));
HttpRequest msg = HttpRequest(HttpMethod.get, url);

await channel.write(msg);

return httpResponseHandler.getResponse(duration).whenComplete(() => channel.close());
}
}

class HttpResponseHandler extends ChannelHandler<HttpResponse> {
final Completer<HttpResponse> _completer = Completer<HttpResponse>();

@override
void channelRead(Channel channel, HttpResponse msg) {
log.i("[${channel.id}] Response ${msg.bodyAsString}");
_completer.complete(msg);
channel.close();
}

Future<HttpResponse> getResponse(Duration duration) {
return _completer.future.timeout(duration);
}
}
2 changes: 1 addition & 1 deletion lib/network/http/codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class HttpRequestCodec extends HttpCodec<HttpRequest> {
@override
HttpRequest createMessage(List<String> reqLine) {
HttpMethod httpMethod = HttpMethod.valueOf(reqLine[0]);
return HttpRequest(httpMethod, reqLine[1], reqLine[2]);
return HttpRequest(httpMethod, reqLine[1], protocolVersion: reqLine[2]);
}

@override
Expand Down
Loading

0 comments on commit 3a67c43

Please sign in to comment.