Fix flashback caused by process

This commit is contained in:
chen08209
2024-06-16 19:04:33 +08:00
parent 05abf2d56d
commit 90bb670442
19 changed files with 316 additions and 459 deletions
@@ -15,6 +15,7 @@ enum class RunState {
class GlobalState {
companion object {
val runState: MutableLiveData<RunState> = MutableLiveData<RunState>(RunState.STOP)
var runTime: Date? = null
var flutterEngine: FlutterEngine? = null
fun getCurrentTilePlugin(): TilePlugin? =
flutterEngine?.plugins?.get(TilePlugin::class.java) as TilePlugin?
@@ -25,6 +25,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.util.Date
class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware {
@@ -93,6 +94,10 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
}
}
"GetRunTimeStamp" -> {
result.success(GlobalState.runTime?.time)
}
"startForeground" -> {
title = call.argument<String>("title") as String
content = call.argument<String>("content") as String
@@ -118,6 +123,7 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
if (GlobalState.runState.value == RunState.START) return;
flClashVpnService?.start(port, props)
GlobalState.runState.value = RunState.START
GlobalState.runTime = Date()
startAfter()
}
@@ -126,6 +132,7 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
flClashVpnService?.stop()
unbindService()
GlobalState.runState.value = RunState.STOP;
GlobalState.runTime = null;
}
@SuppressLint("ForegroundServiceType")
+4 -6
View File
@@ -82,13 +82,13 @@ type Delay struct {
}
type Process struct {
Id int64 `json:"id"`
Metadata constant.Metadata `json:"metadata"`
Id int64 `json:"id"`
Metadata *constant.Metadata `json:"metadata"`
}
type ProcessMapItem struct {
Id int64 `json:"id"`
Value *string `json:"value"`
Id int64 `json:"id"`
Value string `json:"value"`
}
type Now struct {
@@ -396,8 +396,6 @@ func applyConfig(isPatch bool) {
if isPatch {
patchConfig(cfg.General)
} else {
runtime.GC()
executor.Shutdown()
hub.UltraApplyConfig(cfg, true)
}
}
+12 -4
View File
@@ -13,6 +13,7 @@ const (
Now MessageType = "now"
Process MessageType = "process"
Request MessageType = "request"
Run MessageType = "run"
)
type Message struct {
@@ -20,11 +21,18 @@ type Message struct {
Data interface{} `json:"data"`
}
func (message *Message) Json() string {
data, _ := json.Marshal(message)
return string(data)
func (message *Message) Json() (string, error) {
data, err := json.Marshal(message)
return string(data), err
}
func SendMessage(message Message) {
SendToPort(*Port, message.Json())
if Port == nil {
return
}
s, err := message.Json()
if err != nil {
return
}
SendToPort(*Port, s)
}
-42
View File
@@ -1,42 +0,0 @@
//go:build android
package platform
import "syscall"
var nullFd int
var maxFdCount int
func init() {
fd, err := syscall.Open("/dev/null", syscall.O_WRONLY, 0644)
if err != nil {
panic(err.Error())
}
nullFd = fd
var limit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
maxFdCount = 1024
} else {
maxFdCount = int(limit.Cur)
}
maxFdCount = maxFdCount / 4 * 3
}
func ShouldBlockConnection() bool {
fd, err := syscall.Dup(nullFd)
if err != nil {
return true
}
_ = syscall.Close(fd)
if fd > maxFdCount {
return true
}
return false
}
-171
View File
@@ -1,171 +0,0 @@
//go:build android
package platform
import (
"bufio"
"encoding/binary"
"encoding/hex"
"fmt"
"net"
"os"
"strconv"
"strings"
"unsafe"
)
var netIndexOfLocal = -1
var netIndexOfUid = -1
var nativeEndian binary.ByteOrder
func QuerySocketUidFromProcFs(source, _ net.Addr) int {
if netIndexOfLocal < 0 || netIndexOfUid < 0 {
return -1
}
network := source.Network()
if strings.HasSuffix(network, "4") || strings.HasSuffix(network, "6") {
network = network[:len(network)-1]
}
path := "/proc/net/" + network
var sIP net.IP
var sPort int
switch s := source.(type) {
case *net.TCPAddr:
sIP = s.IP
sPort = s.Port
case *net.UDPAddr:
sIP = s.IP
sPort = s.Port
default:
return -1
}
sIP = sIP.To16()
if sIP == nil {
return -1
}
uid := doQuery(path+"6", sIP, sPort)
if uid == -1 {
sIP = sIP.To4()
if sIP == nil {
return -1
}
uid = doQuery(path, sIP, sPort)
}
return uid
}
func doQuery(path string, sIP net.IP, sPort int) int {
file, err := os.Open(path)
if err != nil {
return -1
}
defer file.Close()
reader := bufio.NewReader(file)
var bytes [2]byte
binary.BigEndian.PutUint16(bytes[:], uint16(sPort))
local := fmt.Sprintf("%s:%s", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:]))
for {
row, _, err := reader.ReadLine()
if err != nil {
return -1
}
fields := strings.Fields(string(row))
if len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid {
continue
}
if strings.EqualFold(local, fields[netIndexOfLocal]) {
uid, err := strconv.Atoi(fields[netIndexOfUid])
if err != nil {
return -1
}
return uid
}
}
}
func nativeEndianIP(ip net.IP) []byte {
result := make([]byte, len(ip))
for i := 0; i < len(ip); i += 4 {
value := binary.BigEndian.Uint32(ip[i:])
nativeEndian.PutUint32(result[i:], value)
}
return result
}
func init() {
file, err := os.Open("/proc/net/tcp")
if err != nil {
return
}
defer file.Close()
reader := bufio.NewReader(file)
header, _, err := reader.ReadLine()
if err != nil {
return
}
columns := strings.Fields(string(header))
var txQueue, rxQueue, tr, tmWhen bool
for idx, col := range columns {
offset := 0
if txQueue && rxQueue {
offset--
}
if tr && tmWhen {
offset--
}
switch col {
case "tx_queue":
txQueue = true
case "rx_queue":
rxQueue = true
case "tr":
tr = true
case "tm->when":
tmWhen = true
case "local_address":
netIndexOfLocal = idx + offset
case "uid":
netIndexOfUid = idx + offset
}
}
}
func init() {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
nativeEndian = binary.BigEndian
} else {
nativeEndian = binary.LittleEndian
}
}
+29 -19
View File
@@ -9,16 +9,30 @@ import (
"errors"
"github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"sync"
"sync/atomic"
"time"
)
var (
counter int64
)
type ProcessMap struct {
m sync.Map
}
var processMap = make(map[int64]*string)
func (cm *ProcessMap) Store(key int64, value string) {
cm.m.Store(key, value)
}
func (cm *ProcessMap) Load(key int64) (string, bool) {
value, ok := cm.m.Load(key)
if !ok || value == nil {
return "", false
}
return value.(string), true
}
var counter int64 = 0
var processMap ProcessMap
func init() {
process.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) {
@@ -29,31 +43,24 @@ func init() {
timeout := time.After(200 * time.Millisecond)
message := &bridge.Message{
bridge.SendMessage(bridge.Message{
Type: bridge.Process,
Data: Process{
Id: id,
Metadata: *metadata,
Metadata: metadata,
},
}
bridge.SendMessage(*message)
})
for {
select {
case <-timeout:
return "", errors.New("package resolver timeout")
default:
value, exists := processMap[counter]
value, exists := processMap.Load(id)
if exists {
if value != nil {
log.Infoln("[PKG] %s --> %s by [%s]", metadata.SourceAddress(), metadata.RemoteAddress(), *value)
return *value, nil
} else {
return "", process.ErrInvalidNetwork
}
return value, nil
}
time.Sleep(10 * time.Millisecond)
time.Sleep(20 * time.Millisecond)
}
}
}
@@ -61,12 +68,15 @@ func init() {
//export setProcessMap
func setProcessMap(s *C.char) {
if s == nil {
return
}
go func() {
paramsString := C.GoString(s)
var processMapItem = &ProcessMapItem{}
err := json.Unmarshal([]byte(paramsString), processMapItem)
if err == nil {
processMap[processMapItem.Id] = processMapItem.Value
processMap.Store(processMapItem.Id, processMapItem.Value)
}
}()
}
+5 -26
View File
@@ -4,13 +4,10 @@ package main
import "C"
import (
"core/platform"
t "core/tun"
"errors"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/log"
"golang.org/x/sync/semaphore"
"strconv"
"sync"
"syscall"
"time"
@@ -18,16 +15,11 @@ import (
var tunLock sync.Mutex
var tun *t.Tun
var runTime *time.Time
//export startTUN
func startTUN(fd C.int) {
tunLock.Lock()
now := time.Now()
runTime = &now
go func() {
tunLock.Lock()
defer tunLock.Unlock()
if tun != nil {
@@ -43,6 +35,8 @@ func startTUN(fd C.int) {
closer, err := t.Start(f, gateway, portal, dns)
applyConfig(true)
if err != nil {
log.Errorln("startTUN error: %v", err)
tempTun.Close()
@@ -51,30 +45,18 @@ func startTUN(fd C.int) {
tempTun.Closer = closer
tun = tempTun
applyConfig(true)
}()
}
//export getRunTime
func getRunTime() *C.char {
if runTime == nil {
return C.CString("")
}
return C.CString(strconv.FormatInt(runTime.UnixMilli(), 10))
}
//export stopTun
func stopTun() {
tunLock.Lock()
runTime = nil
go func() {
tunLock.Lock()
defer tunLock.Unlock()
if tun != nil {
tun.Close()
applyConfig(true)
tun = nil
}
}()
@@ -82,9 +64,6 @@ func stopTun() {
func init() {
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
if platform.ShouldBlockConnection() {
return errors.New("blocked")
}
return conn.Control(func(fd uintptr) {
if tun != nil {
tun.MarkSocket(int(fd))
+5
View File
@@ -19,6 +19,8 @@ abstract mixin class ClashMessageListener {
void onRequest(Connection connection) {}
void onNow(Now now) {}
void onRun(String runTime) {}
}
class ClashMessage {
@@ -51,6 +53,9 @@ class ClashMessage {
case MessageType.request:
listener.onRequest(Connection.fromJson(m.data));
break;
case MessageType.run:
listener.onRun(m.data);
break;
}
}
});
+1 -1
View File
@@ -56,7 +56,7 @@ enum ProfileType { file, url }
enum ResultType { success, error }
enum MessageType { log, tun, delay, process, now, request }
enum MessageType { log, tun, delay, process, now, request, run }
enum FindProcessMode { always, off }
+45 -3
View File
@@ -39,7 +39,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
}
}
_buildAppSection() {
Widget _buildAppSection() {
final items = [
if (Platform.isAndroid)
Selector<Config, bool>(
@@ -150,7 +150,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
);
}
_buildGeneralSection() {
Widget _buildGeneralSection() {
final items = [
Selector<ClashConfig, LogLevel>(
selector: (_, clashConfig) => clashConfig.logLevel,
@@ -335,14 +335,56 @@ class _ConfigFragmentState extends State<ConfigFragment> {
);
}
Widget _buildMoreSection() {
final items = [
if (false)
Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.tun.enable,
builder: (_, tunEnable, __) {
return ListItem.switchItem(
leading: const Icon(
Icons.important_devices_outlined
),
title: Text(appLocalizations.tun),
subtitle: Text(appLocalizations.tunDesc),
delegate: SwitchDelegate(
value: tunEnable,
onChanged: (bool value) async {
final clashConfig = context.read<ClashConfig>();
clashConfig.tun = Tun(enable: value);
globalState.appController.updateClashConfigDebounce();
},
),
);
},
),
];
if(items.isEmpty) return Container();
return Section(
title: appLocalizations.general,
child: Column(
children: [
for (final item in items) ...[
item,
if (items.last != item)
const Divider(
height: 0,
)
]
],
),
);
}
@override
Widget build(BuildContext context) {
List<Widget> items = [
_buildAppSection(),
_buildGeneralSection(),
_buildMoreSection(),
];
return ListView.builder(
padding: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.only(bottom: 32),
itemBuilder: (_, index) {
return Container(
alignment: Alignment.center,
+18 -22
View File
@@ -54,21 +54,18 @@ Future<void> vpnService() async {
int.parse(fd),
);
}));
await globalState.init(
appState: appState,
config: config,
clashConfig: clashConfig,
);
if (appState.isInit) {
globalState.applyProfile(
appState: appState,
config: config,
clashConfig: clashConfig,
);
} else {
exit(0);
}
await globalState.applyProfile(
appState: appState,
config: config,
clashConfig: clashConfig,
);
final appLocalizations = await AppLocalizations.load(
other.getLocaleForString(config.locale) ??
@@ -89,19 +86,18 @@ Future<void> vpnService() async {
];
}
if (appState.isInit) {
handleStart();
tile?.addListener(
TileListenerWithVpn(
onStop: () async {
await app?.tip(appLocalizations.stopVpn);
await globalState.stopSystemProxy();
clashCore.shutdown();
exit(0);
},
),
);
}
handleStart();
tile?.addListener(
TileListenerWithVpn(
onStop: () async {
await app?.tip(appLocalizations.stopVpn);
await globalState.stopSystemProxy();
clashCore.shutdown();
exit(0);
},
),
);
}
class ClashMessageListenerWithVpn with ClashMessageListener {
+1 -1
View File
@@ -79,7 +79,7 @@ class Process with _$Process {
class ProcessMapItem with _$ProcessMapItem {
const factory ProcessMapItem({
required int id,
String? value,
required String value,
}) = _ProcessMapItem;
factory ProcessMapItem.fromJson(Map<String, Object?> json) =>
+15 -14
View File
@@ -1013,7 +1013,7 @@ ProcessMapItem _$ProcessMapItemFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$ProcessMapItem {
int get id => throw _privateConstructorUsedError;
String? get value => throw _privateConstructorUsedError;
String get value => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -1027,7 +1027,7 @@ abstract class $ProcessMapItemCopyWith<$Res> {
ProcessMapItem value, $Res Function(ProcessMapItem) then) =
_$ProcessMapItemCopyWithImpl<$Res, ProcessMapItem>;
@useResult
$Res call({int id, String? value});
$Res call({int id, String value});
}
/// @nodoc
@@ -1044,17 +1044,17 @@ class _$ProcessMapItemCopyWithImpl<$Res, $Val extends ProcessMapItem>
@override
$Res call({
Object? id = null,
Object? value = freezed,
Object? value = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
value: freezed == value
value: null == value
? _value.value
: value // ignore: cast_nullable_to_non_nullable
as String?,
as String,
) as $Val);
}
}
@@ -1067,7 +1067,7 @@ abstract class _$$ProcessMapItemImplCopyWith<$Res>
__$$ProcessMapItemImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int id, String? value});
$Res call({int id, String value});
}
/// @nodoc
@@ -1082,17 +1082,17 @@ class __$$ProcessMapItemImplCopyWithImpl<$Res>
@override
$Res call({
Object? id = null,
Object? value = freezed,
Object? value = null,
}) {
return _then(_$ProcessMapItemImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
value: freezed == value
value: null == value
? _value.value
: value // ignore: cast_nullable_to_non_nullable
as String?,
as String,
));
}
}
@@ -1100,7 +1100,7 @@ class __$$ProcessMapItemImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$ProcessMapItemImpl implements _ProcessMapItem {
const _$ProcessMapItemImpl({required this.id, this.value});
const _$ProcessMapItemImpl({required this.id, required this.value});
factory _$ProcessMapItemImpl.fromJson(Map<String, dynamic> json) =>
_$$ProcessMapItemImplFromJson(json);
@@ -1108,7 +1108,7 @@ class _$ProcessMapItemImpl implements _ProcessMapItem {
@override
final int id;
@override
final String? value;
final String value;
@override
String toString() {
@@ -1144,8 +1144,9 @@ class _$ProcessMapItemImpl implements _ProcessMapItem {
}
abstract class _ProcessMapItem implements ProcessMapItem {
const factory _ProcessMapItem({required final int id, final String? value}) =
_$ProcessMapItemImpl;
const factory _ProcessMapItem(
{required final int id,
required final String value}) = _$ProcessMapItemImpl;
factory _ProcessMapItem.fromJson(Map<String, dynamic> json) =
_$ProcessMapItemImpl.fromJson;
@@ -1153,7 +1154,7 @@ abstract class _ProcessMapItem implements ProcessMapItem {
@override
int get id;
@override
String? get value;
String get value;
@override
@JsonKey(ignore: true)
_$$ProcessMapItemImplCopyWith<_$ProcessMapItemImpl> get copyWith =>
+2 -1
View File
@@ -57,6 +57,7 @@ const _$MessageTypeEnumMap = {
MessageType.process: 'process',
MessageType.now: 'now',
MessageType.request: 'request',
MessageType.run: 'run',
};
_$DelayImpl _$$DelayImplFromJson(Map<String, dynamic> json) => _$DelayImpl(
@@ -95,7 +96,7 @@ Map<String, dynamic> _$$ProcessImplToJson(_$ProcessImpl instance) =>
_$ProcessMapItemImpl _$$ProcessMapItemImplFromJson(Map<String, dynamic> json) =>
_$ProcessMapItemImpl(
id: (json['id'] as num).toInt(),
value: json['value'] as String?,
value: json['value'] as String,
);
Map<String, dynamic> _$$ProcessMapItemImplToJson(
+16 -1
View File
@@ -56,6 +56,10 @@ class Proxy extends ProxyPlatform {
return await methodChannel.invokeMethod<bool?>("SetProtect", {'fd': fd});
}
Future<int?> getRunTimeStamp() async {
return await methodChannel.invokeMethod<int?>("GetRunTimeStamp");
}
Future<bool?> startForeground({
required String title,
required String content,
@@ -75,8 +79,19 @@ class Proxy extends ProxyPlatform {
}
}
// updateStartTime() async {
// startTime = clashCore.getRunTime();
// }
updateStartTime() async {
startTime = clashCore.getRunTime();
startTime = await getRunTime();
}
Future<DateTime?> getRunTime() async {
final runTimeStamp = await getRunTimeStamp();
return runTimeStamp != null
? DateTime.fromMillisecondsSinceEpoch(runTimeStamp)
: null;
}
}
+8 -1
View File
@@ -1,6 +1,7 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/plugins/proxy.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:fl_clash/plugins/app.dart';
@@ -61,7 +62,7 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
clashCore.setProcessMap(
ProcessMapItem(
id: process.id,
value: packageName,
value: packageName ?? "",
),
);
super.onProcess(process);
@@ -72,4 +73,10 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
globalState.appController.appState.addRequest(connection);
super.onRequest(connection);
}
@override
void onRun(String runTime) async {
// proxy?.updateStartTime();
super.onRun(runTime);
}
}
+146 -146
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.23
version: 0.8.24
environment:
sdk: '>=3.1.0 <4.0.0'