//go:build android && cgo package main import "C" import ( bridge "core/dart-bridge" "core/platform" "core/state" t "core/tun" "encoding/json" "errors" "fmt" "github.com/metacubex/mihomo/common/utils" "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" "strconv" "strings" "sync" "syscall" "time" ) type Fd struct { Id string `json:"id"` Value int64 `json:"value"` } type Process struct { Id string `json:"id"` Metadata *constant.Metadata `json:"metadata"` } type ProcessMapItem struct { Id string `json:"id"` Value string `json:"value"` } type InvokeManager struct { invokeMap sync.Map chanMap map[string]chan struct{} chanLock sync.Mutex } func NewInvokeManager() *InvokeManager { return &InvokeManager{ chanMap: make(map[string]chan struct{}), } } func (m *InvokeManager) completer(id string, value string) { m.invokeMap.Store(id, value) m.chanLock.Lock() if ch, ok := m.chanMap[id]; ok { close(ch) delete(m.chanMap, id) } m.chanLock.Unlock() } func (m *InvokeManager) await(id string) string { m.chanLock.Lock() if _, ok := m.chanMap[id]; !ok { m.chanMap[id] = make(chan struct{}) } ch := m.chanMap[id] m.chanLock.Unlock() timeout := time.After(500 * time.Millisecond) select { case <-ch: res, ok := m.invokeMap.Load(id) m.invokeMap.Delete(id) if ok { return res.(string) } else { return "" } case <-timeout: m.completer(id, "") return "" } } var ( invokePort int64 = -1 tunListener *sing_tun.Listener fdInvokeMap = NewInvokeManager() processInvokeMap = NewInvokeManager() tunLock sync.Mutex runTime *time.Time errBlocked = errors.New("blocked") ) func handleStartTun(fd int) string { handleStopTun() tunLock.Lock() defer tunLock.Unlock() if fd == 0 { now := time.Now() runTime = &now } else { initSocketHook() tunListener, _ = t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack) if tunListener != nil { log.Infoln("TUN address: %v", tunListener.Address()) } now := time.Now() runTime = &now } return handleGetRunTime() } func handleStopTun() { tunLock.Lock() defer tunLock.Unlock() removeSocketHook() runTime = nil if tunListener != nil { log.Infoln("TUN close") _ = tunListener.Close() } } func handleGetRunTime() string { if runTime == nil { return "" } return strconv.FormatInt(runTime.UnixMilli(), 10) } func handleSetProcessMap(params string) { var processMapItem = &ProcessMapItem{} err := json.Unmarshal([]byte(params), processMapItem) if err == nil { processInvokeMap.completer(processMapItem.Id, processMapItem.Value) } } //export attachInvokePort func attachInvokePort(mPort C.longlong) { invokePort = int64(mPort) } func sendInvokeMessage(message InvokeMessage) { if invokePort == -1 { return } bridge.SendToPort(invokePort, message.Json()) } func handleMarkSocket(fd Fd) { sendInvokeMessage(InvokeMessage{ Type: ProtectInvoke, Data: fd, }) } func handleParseProcess(process Process) { sendInvokeMessage(InvokeMessage{ Type: ProcessInvoke, Data: process, }) } func handleSetFdMap(id string) { go func() { fdInvokeMap.completer(id, "") }() } func initSocketHook() { dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error { if platform.ShouldBlockConnection() { return errBlocked } return conn.Control(func(fd uintptr) { fdInt := int64(fd) id := utils.NewUUIDV1().String() handleMarkSocket(Fd{ Id: id, Value: fdInt, }) fdInvokeMap.await(id) }) } } func removeSocketHook() { dialer.DefaultSocketHook = nil } func init() { process.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) { if metadata == nil { return "", process.ErrInvalidNetwork } id := utils.NewUUIDV1().String() handleParseProcess(Process{ Id: id, Metadata: metadata, }) return processInvokeMap.await(id), nil } } func handleGetAndroidVpnOptions() string { tunLock.Lock() defer tunLock.Unlock() options := state.AndroidVpnOptions{ Enable: state.CurrentState.VpnProps.Enable, Port: currentConfig.General.MixedPort, Ipv4Address: state.DefaultIpv4Address, Ipv6Address: state.GetIpv6Address(), AccessControl: state.CurrentState.VpnProps.AccessControl, SystemProxy: state.CurrentState.VpnProps.SystemProxy, AllowBypass: state.CurrentState.VpnProps.AllowBypass, RouteAddress: currentConfig.General.Tun.RouteAddress, BypassDomain: state.CurrentState.BypassDomain, DnsServerAddress: state.GetDnsServerAddress(), } data, err := json.Marshal(options) if err != nil { fmt.Println("Error:", err) return "" } return string(data) } func handleUpdateDns(value string) { go func() { log.Infoln("[DNS] updateDns %s", value) dns.UpdateSystemDNS(strings.Split(value, ",")) dns.FlushCacheWithDefaultResolver() }() } func handleGetCurrentProfileName() string { if state.CurrentState == nil { return "" } return state.CurrentState.CurrentProfileName } func nextHandle(action *Action, result func(data interface{})) bool { switch action.Method { case startTunMethod: data := action.Data.(string) var fd int _ = json.Unmarshal([]byte(data), &fd) result(handleStartTun(fd)) return true case stopTunMethod: handleStopTun() result(true) return true case getAndroidVpnOptionsMethod: result(handleGetAndroidVpnOptions()) return true case updateDnsMethod: data := action.Data.(string) handleUpdateDns(data) result(true) return true case setFdMapMethod: fdId := action.Data.(string) handleSetFdMap(fdId) result(true) return true case setProcessMapMethod: data := action.Data.(string) handleSetProcessMap(data) result(true) return true case getRunTimeMethod: result(handleGetRunTime()) return true case getCurrentProfileNameMethod: result(handleGetCurrentProfileName()) return true } return false } //export quickStart func quickStart(dirChar *C.char, paramsChar *C.char, stateParamsChar *C.char, port C.longlong) { i := int64(port) dir := C.GoString(dirChar) bytes := []byte(C.GoString(paramsChar)) stateParams := C.GoString(stateParamsChar) go func() { res := handleInitClash(dir) if res == false { bridge.SendToPort(i, "init error") } handleSetState(stateParams) bridge.SendToPort(i, handleUpdateConfig(bytes)) }() } //export startTUN func startTUN(fd C.int) *C.char { f := int(fd) return C.CString(handleStartTun(f)) } //export getRunTime func getRunTime() *C.char { return C.CString(handleGetRunTime()) } //export stopTun func stopTun() { handleStopTun() } //export setFdMap func setFdMap(fdIdChar *C.char) { fdId := C.GoString(fdIdChar) handleSetFdMap(fdId) } //export getCurrentProfileName func getCurrentProfileName() *C.char { return C.CString(handleGetCurrentProfileName()) } //export getAndroidVpnOptions func getAndroidVpnOptions() *C.char { return C.CString(handleGetAndroidVpnOptions()) } //export setState func setState(s *C.char) { paramsString := C.GoString(s) handleSetState(paramsString) } //export updateDns func updateDns(s *C.char) { dnsList := C.GoString(s) handleUpdateDns(dnsList) } //export setProcessMap func setProcessMap(s *C.char) { if s == nil { return } paramsString := C.GoString(s) handleSetProcessMap(paramsString) }