Skip to content

Commit

Permalink
安卓抓flutter请求
Browse files Browse the repository at this point in the history
  • Loading branch information
wanghongenpin committed Nov 19, 2023
1 parent c209ffc commit 2f2e5b4
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 18 deletions.
5 changes: 5 additions & 0 deletions lib/native/vpn.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ import 'package:flutter/services.dart';
class Vpn {
static const MethodChannel proxyVpnChannel = MethodChannel('com.proxy/proxyVpn');

static bool isVpnStarted = false; //vpn是否已经启动

static startVpn(String host, int port, [List<String>? appList]) {
proxyVpnChannel.invokeMethod("startVpn", {"proxyHost": host, "proxyPort": port, "allowApps": appList});
isVpnStarted = true;
}

static stopVpn() {
proxyVpnChannel.invokeMethod("stopVpn");
isVpnStarted = false;
}

//重启vpn
static restartVpn(String host, int port, [List<String>? appList]) {
proxyVpnChannel.invokeMethod("restartVpn", {"proxyHost": host, "proxyPort": port, "allowApps": appList});
isVpnStarted = true;
}
}
2 changes: 1 addition & 1 deletion lib/network/bin/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class ProxyServer {
channel.pipeline.handle(
HttpRequestCodec(),
HttpResponseCodec(),
HttpChannelHandler(
HttpProxyChannelHandler(
listener: CombinedEventListener(listeners), requestRewrites: configuration.requestRewrites));
});

Expand Down
5 changes: 4 additions & 1 deletion lib/network/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class Channel {
pipeline.listen(this);
}

///是否是ssl链接
bool get isSsl => _socket is SecureSocket;

Future<void> write(Object obj) async {
if (isClosed) {
logger.w("[$id] channel is closed");
Expand Down Expand Up @@ -214,7 +217,7 @@ class ChannelPipeline extends ChannelHandler<Uint8List> {
buffer.clear();

if (data is HttpRequest) {
data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data);
data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data, ssl: channel.isSsl);
if (data.headers.host != null && data.headers.host?.contains(":") == false) {
data.hostAndPort?.host = data.headers.host!;
}
Expand Down
9 changes: 6 additions & 3 deletions lib/network/handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ abstract class EventListener {
}

/// http请求处理器
class HttpChannelHandler extends ChannelHandler<HttpRequest> {
class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
EventListener? listener;
RequestRewrites? requestRewrites;

HttpChannelHandler({this.listener, this.requestRewrites});
HttpProxyChannelHandler({this.listener, this.requestRewrites});

@override
void channelRead(Channel channel, HttpRequest msg) async {
Expand Down Expand Up @@ -202,7 +202,7 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
return remoteChannel;
}

var hostAndPort = getHostAndPort(httpRequest);
var hostAndPort = httpRequest.hostAndPort ?? getHostAndPort(httpRequest);
clientChannel.putAttribute(AttributeKeys.host, hostAndPort);

var proxyHandler = HttpResponseProxyHandler(clientChannel, listener: listener, requestRewrites: requestRewrites);
Expand All @@ -226,6 +226,9 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
if (httpRequest.method == HttpMethod.connect) {
await clientChannel.write(
HttpResponse(HttpStatus.ok.reason('Connection established'), protocolVersion: httpRequest.protocolVersion));
} else if (clientChannel.isSsl) {
proxyChannel.secureSocket = await SecureSocket.secure(proxyChannel.socket,
host: hostAndPort.host, onBadCertificate: (certificate) => true);
}
return proxyChannel;
}
Expand Down
5 changes: 2 additions & 3 deletions lib/network/host_port.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/http/http_headers.dart';

/// 获取主机和端口
HostAndPort getHostAndPort(HttpRequest request) {
HostAndPort getHostAndPort(HttpRequest request, {bool? ssl}) {
String requestUri = request.uri;
//有些请求直接是路径 /xxx, 从header取host
if (request.uri.startsWith("/")) {
requestUri = request.headers.get(HttpHeaders.HOST)!;
}

return HostAndPort.of(requestUri);
return HostAndPort.of(requestUri, ssl: ssl);
}

class HostAndPort {
Expand Down
14 changes: 8 additions & 6 deletions lib/network/network.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'package:network_proxy/network/handler.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/util/crts.dart';
import 'package:network_proxy/network/util/host_filter.dart';
import 'package:network_proxy/network/util/tls.dart';
import 'package:network_proxy/utils/platform.dart';

import 'host_port.dart';
Expand Down Expand Up @@ -70,29 +71,30 @@ class Network {
}

//ssl握手
if (hostAndPort?.isSsl() == true || (data.length > 3 && data.first == 0x16 && data[1] == 0x03 && data[2] == 0x01)) {
if (hostAndPort?.isSsl() == true || (data.length > 2 && data.first == 0x16 && data[1] == 0x03)) {
if (hostAndPort?.scheme == HostAndPort.httpScheme) {
hostAndPort?.scheme = HostAndPort.httpsScheme;
}

ssl(channel, hostAndPort!, data);
ssl(channel, hostAndPort, data);
return;
}

channel.pipeline.channelRead(channel, data);
}

/// ssl握手
void ssl(Channel channel, HostAndPort hostAndPort, Uint8List data) async {
void ssl(Channel channel, HostAndPort? hostAndPort, Uint8List data) async {
try {
Channel? remoteChannel = channel.getAttribute(channel.id);
if (remoteChannel != null) {
remoteChannel.secureSocket = await SecureSocket.secure(remoteChannel.socket,
host: hostAndPort.host, onBadCertificate: (certificate) => true);
host: hostAndPort?.host, onBadCertificate: (certificate) => true);
}
String? host = hostAndPort?.host;
host ??= TLS.getDomain(data);

//ssl自签证书
var certificate = await CertificateManager.getCertificateContext(hostAndPort.host);
var certificate = await CertificateManager.getCertificateContext(host!);
//服务端等待客户端ssl握手
channel.secureSocket = await SecureSocket.secureServer(channel.socket, certificate, bufferedData: data);
} catch (error, trace) {
Expand Down
52 changes: 52 additions & 0 deletions lib/network/util/tls.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:typed_data';

class TLS {
///从TLS Client Hello 解析域名
static String? getDomain(Uint8List data) {
try {
int sessionLength = data[43];
int pos = 44 + sessionLength;
if (data.length < pos + 2) return null;

int cipherSuitesLength = data.buffer.asByteData().getUint16(pos);
pos += 2 + cipherSuitesLength;
if (data.length < pos + 1) return null;

int compressionMethodsLength = data[pos];
pos += 1 + compressionMethodsLength;
if (data.length < pos + 2) return null;

int extensionsLength = data.buffer.asByteData().getUint16(pos);
pos += 2;
if (data.length < pos + extensionsLength) return null;

int end = pos + extensionsLength;
while (pos + 4 <= end) {
int extensionType = data.buffer.asByteData().getUint16(pos);
int extensionLength = data.buffer.asByteData().getUint16(pos + 2);
pos += 4;

if (extensionType == 0 /* server_name */) {
if (pos + 5 > end) return null;
int serverNameListLength = data.buffer.asByteData().getUint16(pos);
pos += 2;
if (pos + serverNameListLength > end) return null;

int serverNameType = data[pos];
int serverNameLength = data.buffer.asByteData().getUint16(pos + 1);
pos += 3;
if (serverNameType != 0 /* host_name */) return null;
if (pos + serverNameLength > end) return null;

return String.fromCharCodes(data.sublist(pos, pos + serverNameLength));
} else {
pos += extensionLength;
}
}
} catch (_) {
// Ignore errors, just return null
}

return null;
}
}
4 changes: 2 additions & 2 deletions lib/ui/mobile/request/history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class MobileHistory extends StatefulWidget {
final ProxyServer proxyServer;
final GlobalKey<RequestListState> requestStateKey;

const MobileHistory({Key? key, required this.proxyServer, required this.requestStateKey}) : super(key: key);
const MobileHistory({super.key, required this.proxyServer, required this.requestStateKey});

@override
State<StatefulWidget> createState() {
Expand Down Expand Up @@ -92,7 +92,7 @@ class _MobileHistoryState extends State<MobileHistory> {

//导入har
import(HistoryStorage storage) async {
const XTypeGroup typeGroup = XTypeGroup(label: 'Har', extensions: <String>['har']);
const XTypeGroup typeGroup = XTypeGroup(label: 'har', extensions: <String>['har'], uniformTypeIdentifiers: ["public.item"]);
final XFile? file = await openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
if (file == null) {
return;
Expand Down
6 changes: 4 additions & 2 deletions lib/ui/mobile/request/request_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_toastr/flutter_toastr.dart';
import 'package:network_proxy/native/vpn.dart';
import 'package:network_proxy/network/bin/server.dart';
import 'package:network_proxy/network/host_port.dart';
import 'package:network_proxy/network/http/http.dart';
Expand Down Expand Up @@ -123,11 +124,12 @@ class RequestEditorState extends State<MobileRequestEditor> with SingleTickerPro
var headers = requestKey.currentState?.getHeaders();
var requestBody = requestKey.currentState?.getBody();

HttpRequest request = HttpRequest(HttpMethod.valueOf(currentState.requestMethod), Uri.encodeFull(currentState.requestUrl));
HttpRequest request =
HttpRequest(HttpMethod.valueOf(currentState.requestMethod), Uri.encodeFull(currentState.requestUrl));
request.headers.addAll(headers);
request.body = requestBody == null ? null : utf8.encode(requestBody);

var proxyInfo = widget.proxyServer?.isRunning == true ? ProxyInfo.of("127.0.0.1", widget.proxyServer!.port) : null;
var proxyInfo = Vpn.isVpnStarted ? ProxyInfo.of("127.0.0.1", widget.proxyServer!.port) : null;
HttpClients.proxyRequest(proxyInfo: proxyInfo, request).then((response) {
FlutterToastr.show('请求成功', context);
this.response = response;
Expand Down

0 comments on commit 2f2e5b4

Please sign in to comment.