Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies
254 lines
5.2 KiB
Go
254 lines
5.2 KiB
Go
//go:build cgo
|
|
|
|
package main
|
|
|
|
/*
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"context"
|
|
"core/platform"
|
|
t "core/tun"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/metacubex/mihomo/component/dialer"
|
|
"github.com/metacubex/mihomo/component/process"
|
|
"github.com/metacubex/mihomo/constant"
|
|
"github.com/metacubex/mihomo/dns"
|
|
"github.com/metacubex/mihomo/listener/sing_tun"
|
|
"github.com/metacubex/mihomo/log"
|
|
"golang.org/x/sync/semaphore"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var messageCallback unsafe.Pointer
|
|
|
|
type TunHandler struct {
|
|
listener *sing_tun.Listener
|
|
callback unsafe.Pointer
|
|
|
|
limit *semaphore.Weighted
|
|
}
|
|
|
|
func (th *TunHandler) start(fd int, stack, address, dns string) {
|
|
_ = th.limit.Acquire(context.TODO(), 4)
|
|
defer th.limit.Release(4)
|
|
th.initHook()
|
|
tunListener := t.Start(fd, stack, address, dns)
|
|
if tunListener != nil {
|
|
log.Infoln("TUN address: %v", tunListener.Address())
|
|
th.listener = tunListener
|
|
return
|
|
}
|
|
th.clear()
|
|
}
|
|
|
|
func (th *TunHandler) close() {
|
|
_ = th.limit.Acquire(context.TODO(), 4)
|
|
defer th.limit.Release(4)
|
|
th.clear()
|
|
}
|
|
|
|
func (th *TunHandler) clear() {
|
|
th.removeHook()
|
|
if th.listener != nil {
|
|
_ = th.listener.Close()
|
|
}
|
|
if th.callback != nil {
|
|
releaseObject(th.callback)
|
|
}
|
|
th.callback = nil
|
|
th.listener = nil
|
|
}
|
|
|
|
func (th *TunHandler) handleProtect(fd int) {
|
|
_ = th.limit.Acquire(context.Background(), 1)
|
|
defer th.limit.Release(1)
|
|
|
|
if th.listener == nil {
|
|
return
|
|
}
|
|
|
|
protect(th.callback, fd)
|
|
}
|
|
|
|
func (th *TunHandler) handleResolveProcess(source, target net.Addr) string {
|
|
_ = th.limit.Acquire(context.Background(), 1)
|
|
defer th.limit.Release(1)
|
|
|
|
if th.listener == nil {
|
|
return ""
|
|
}
|
|
var protocol int
|
|
uid := -1
|
|
switch source.Network() {
|
|
case "udp", "udp4", "udp6":
|
|
protocol = syscall.IPPROTO_UDP
|
|
case "tcp", "tcp4", "tcp6":
|
|
protocol = syscall.IPPROTO_TCP
|
|
}
|
|
if version < 29 {
|
|
uid = platform.QuerySocketUidFromProcFs(source, target)
|
|
}
|
|
return resolveProcess(th.callback, protocol, source.String(), target.String(), uid)
|
|
}
|
|
|
|
func (th *TunHandler) initHook() {
|
|
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
|
|
if platform.ShouldBlockConnection() {
|
|
return errBlocked
|
|
}
|
|
return conn.Control(func(fd uintptr) {
|
|
tunHandler.handleProtect(int(fd))
|
|
})
|
|
}
|
|
process.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) {
|
|
src, dst := metadata.RawSrcAddr, metadata.RawDstAddr
|
|
if src == nil || dst == nil {
|
|
return "", process.ErrInvalidNetwork
|
|
}
|
|
return tunHandler.handleResolveProcess(src, dst), nil
|
|
}
|
|
}
|
|
|
|
func (th *TunHandler) removeHook() {
|
|
dialer.DefaultSocketHook = nil
|
|
process.DefaultPackageNameResolver = nil
|
|
}
|
|
|
|
var (
|
|
tunLock sync.Mutex
|
|
errBlocked = errors.New("blocked")
|
|
tunHandler *TunHandler
|
|
)
|
|
|
|
func handleStopTun() {
|
|
tunLock.Lock()
|
|
defer tunLock.Unlock()
|
|
if tunHandler != nil {
|
|
tunHandler.close()
|
|
}
|
|
}
|
|
|
|
func handleStartTun(callback unsafe.Pointer, fd int, stack, address, dns string) {
|
|
handleStopTun()
|
|
tunLock.Lock()
|
|
defer tunLock.Unlock()
|
|
if fd != 0 {
|
|
tunHandler = &TunHandler{
|
|
callback: callback,
|
|
limit: semaphore.NewWeighted(4),
|
|
}
|
|
tunHandler.start(fd, stack, address, dns)
|
|
}
|
|
}
|
|
|
|
func handleUpdateDns(value string) {
|
|
go func() {
|
|
log.Infoln("[DNS] updateDns %s", value)
|
|
dns.UpdateSystemDNS(strings.Split(value, ","))
|
|
dns.FlushCacheWithDefaultResolver()
|
|
}()
|
|
}
|
|
|
|
func (result ActionResult) send() {
|
|
data, err := result.Json()
|
|
if err != nil {
|
|
return
|
|
}
|
|
invokeResult(result.callback, string(data))
|
|
if result.Method != messageMethod {
|
|
releaseObject(result.callback)
|
|
}
|
|
}
|
|
|
|
func nextHandle(action *Action, result ActionResult) bool {
|
|
switch action.Method {
|
|
case updateDnsMethod:
|
|
data := action.Data.(string)
|
|
handleUpdateDns(data)
|
|
result.success(true)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
//export invokeAction
|
|
func invokeAction(callback unsafe.Pointer, paramsChar *C.char) {
|
|
params := parseCString(paramsChar)
|
|
var action = &Action{}
|
|
err := json.Unmarshal([]byte(params), action)
|
|
if err != nil {
|
|
invokeResult(callback, err.Error())
|
|
return
|
|
}
|
|
result := ActionResult{
|
|
Id: action.Id,
|
|
Method: action.Method,
|
|
callback: callback,
|
|
}
|
|
go handleAction(action, result)
|
|
}
|
|
|
|
//export startTUN
|
|
func startTUN(callback unsafe.Pointer, fd C.int, stackChar, addressChar, dnsChar *C.char) bool {
|
|
handleStartTun(callback, int(fd), parseCString(stackChar), parseCString(addressChar), parseCString(dnsChar))
|
|
return true
|
|
}
|
|
|
|
//export setMessageCallback
|
|
func setMessageCallback(callback unsafe.Pointer) {
|
|
if messageCallback != nil {
|
|
releaseObject(messageCallback)
|
|
}
|
|
messageCallback = callback
|
|
}
|
|
|
|
//export getTotalTraffic
|
|
func getTotalTraffic(onlyStatisticsProxy bool) *C.char {
|
|
return C.CString(handleGetTotalTraffic(onlyStatisticsProxy))
|
|
}
|
|
|
|
//export getTraffic
|
|
func getTraffic(onlyStatisticsProxy bool) *C.char {
|
|
return C.CString(handleGetTraffic(onlyStatisticsProxy))
|
|
}
|
|
|
|
func sendMessage(message Message) {
|
|
if messageCallback == nil {
|
|
return
|
|
}
|
|
result := ActionResult{
|
|
Method: messageMethod,
|
|
callback: messageCallback,
|
|
Data: message,
|
|
}
|
|
result.send()
|
|
}
|
|
|
|
//export stopTun
|
|
func stopTun() {
|
|
handleStopTun()
|
|
}
|
|
|
|
//export suspend
|
|
func suspend(suspended bool) {
|
|
handleSuspend(suspended)
|
|
}
|
|
|
|
//export forceGC
|
|
func forceGC() {
|
|
handleForceGC()
|
|
}
|
|
|
|
//export updateDns
|
|
func updateDns(s *C.char) {
|
|
handleUpdateDns(parseCString(s))
|
|
}
|