Files
proxypin/lib/network/handler.dart

231 lines
7.6 KiB
Dart

/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:network_proxy/network/host_port.dart';
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_client.dart';
///请求和响应事件监听
abstract class EventListener {
void onRequest(Channel channel, HttpRequest request);
void onResponse(Channel channel, HttpResponse response);
}
/// http请求处理器
class HttpChannelHandler extends ChannelHandler<HttpRequest> {
EventListener? listener;
RequestRewrites? requestRewrites;
HttpChannelHandler({this.listener, this.requestRewrites});
@override
void channelRead(Channel channel, HttpRequest msg) async {
channel.putAttribute(AttributeKeys.request, msg);
if (msg.uri == 'http://proxy.pin/ssl' || msg.requestUrl == 'http://127.0.0.1:${channel.socket.port}/ssl') {
_crtDownload(channel, msg);
return;
}
//请求本服务
if ((await localIps()).contains(msg.hostAndPort?.host)) {
localRequest(msg, channel);
return;
}
//转发请求
forward(channel, msg).catchError((error, trace) {
channel.close();
if (error is SocketException &&
(error.message.contains("Failed host lookup") || error.message.contains("Connection timed out"))) {
log.e("连接失败 ${error.message}");
return;
}
log.e("转发请求失败", error: error, stackTrace: trace);
});
}
@override
void channelInactive(Channel channel) {
Channel? remoteChannel = channel.getAttribute(channel.id);
remoteChannel?.close();
}
//请求本服务
localRequest(HttpRequest msg, Channel channel) async {
//获取配置
if (msg.path() == '/config') {
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;
}
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);
}
/// 转发请求
Future<void> forward(Channel channel, HttpRequest httpRequest) async {
//获取远程连接
var remoteChannel = await _getRemoteChannel(channel, httpRequest);
//实现抓包代理转发
if (httpRequest.method != HttpMethod.connect) {
// log.i("[${channel.id}] ${httpRequest.method.name} ${httpRequest.requestUrl}");
var replaceBody = requestRewrites?.findRequestReplaceWith(httpRequest.hostAndPort?.host, httpRequest.path());
if (replaceBody?.isNotEmpty == true) {
httpRequest.body = utf8.encode(replaceBody!);
}
if (!HostFilter.filter(httpRequest.hostAndPort?.host)) {
listener?.onRequest(channel, httpRequest);
}
await remoteChannel.write(httpRequest);
}
}
void _crtDownload(Channel channel, HttpRequest request) async {
const String fileMimeType = 'application/x-x509-ca-cert';
var response = HttpResponse(request.protocolVersion, HttpStatus.ok);
response.headers.set(HttpHeaders.CONTENT_TYPE, fileMimeType);
response.headers.set("Content-Disposition", 'inline;filename=ProxyPinCA.crt');
response.headers.set("Connection", 'close');
var body = await FileRead.read('assets/certs/ca.crt');
response.headers.set("Content-Length", body.lengthInBytes.toString());
if (request.method == HttpMethod.head) {
channel.writeAndClose(response);
return;
}
response.body = body.buffer.asUint8List();
channel.writeAndClose(response);
}
/// 获取远程连接
Future<Channel> _getRemoteChannel(Channel clientChannel, HttpRequest httpRequest) async {
String clientId = clientChannel.id;
//客户端连接 作为缓存
Channel? remoteChannel = clientChannel.getAttribute(clientId);
if (remoteChannel != null) {
return remoteChannel;
}
var hostAndPort = getHostAndPort(httpRequest);
clientChannel.putAttribute(AttributeKeys.host, hostAndPort);
var proxyHandler = HttpResponseProxyHandler(clientChannel, listener: listener, requestRewrites: requestRewrites);
//远程转发
HostAndPort? remote = clientChannel.getAttribute(AttributeKeys.remote);
//外部代理
ProxyInfo? proxyInfo = clientChannel.getAttribute(AttributeKeys.proxyInfo);
if (remote != null || proxyInfo != null) {
HostAndPort connectHost = remote ?? HostAndPort.host(proxyInfo!.host, proxyInfo.port!);
var proxyChannel = await HttpClients.startConnect(connectHost, proxyHandler);
clientChannel.putAttribute(clientId, proxyChannel);
proxyChannel.write(httpRequest);
return proxyChannel;
}
var proxyChannel = await HttpClients.startConnect(hostAndPort, proxyHandler);
clientChannel.putAttribute(clientId, proxyChannel);
//https代理新建连接请求
if (httpRequest.method == HttpMethod.connect) {
await clientChannel
.write(HttpResponse(httpRequest.protocolVersion, HttpStatus.ok.reason('Connection established')));
}
return proxyChannel;
}
}
/// http响应代理
class HttpResponseProxyHandler extends ChannelHandler<HttpResponse> {
//客户端的连接
final Channel clientChannel;
EventListener? listener;
RequestRewrites? requestRewrites;
HttpResponseProxyHandler(this.clientChannel, {this.listener, this.requestRewrites});
@override
void channelRead(Channel channel, HttpResponse msg) async {
msg.request = clientChannel.getAttribute(AttributeKeys.request);
msg.request?.response = msg;
// log.i("[${clientChannel.id}] Response ${msg}");
var replaceBody = requestRewrites?.findResponseReplaceWith(msg.request?.hostAndPort?.host, msg.request?.path());
if (replaceBody?.isNotEmpty == true) {
msg.body = utf8.encode(replaceBody!);
}
if (!HostFilter.filter(msg.request?.hostAndPort?.host)) {
listener?.onResponse(clientChannel, msg);
}
//发送给客户端
await clientChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
clientChannel.close();
}
}
class RelayHandler extends ChannelHandler<Object> {
final Channel remoteChannel;
RelayHandler(this.remoteChannel);
@override
void channelRead(Channel channel, Object msg) {
//发送给客户端
remoteChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
remoteChannel.close();
}
}