181 lines
2.8 KiB
Go
181 lines
2.8 KiB
Go
//go:build android
|
|
|
|
package main
|
|
|
|
import "C"
|
|
import (
|
|
"core/platform"
|
|
t "core/tun"
|
|
"errors"
|
|
"strconv"
|
|
"sync"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/metacubex/mihomo/component/dialer"
|
|
"github.com/metacubex/mihomo/log"
|
|
"golang.org/x/sync/semaphore"
|
|
)
|
|
|
|
var tunLock sync.Mutex
|
|
var tun *t.Tun
|
|
var runTime *time.Time
|
|
|
|
type FdMap struct {
|
|
m sync.Map
|
|
}
|
|
|
|
func (cm *FdMap) Store(key int64) {
|
|
cm.m.Store(key, struct{}{})
|
|
}
|
|
|
|
func (cm *FdMap) Load(key int64) bool {
|
|
_, ok := cm.m.Load(key)
|
|
return ok
|
|
}
|
|
|
|
var fdMap FdMap
|
|
|
|
//export startTUN
|
|
func startTUN(fd C.int, port C.longlong) {
|
|
i := int64(port)
|
|
ServicePort = i
|
|
if fd == 0 {
|
|
tunLock.Lock()
|
|
defer tunLock.Unlock()
|
|
now := time.Now()
|
|
runTime = &now
|
|
SendMessage(Message{
|
|
Type: StartedMessage,
|
|
Data: strconv.FormatInt(runTime.UnixMilli(), 10),
|
|
})
|
|
return
|
|
}
|
|
initSocketHook()
|
|
go func() {
|
|
tunLock.Lock()
|
|
defer tunLock.Unlock()
|
|
|
|
if tun != nil {
|
|
tun.Close()
|
|
tun = nil
|
|
}
|
|
|
|
f := int(fd)
|
|
gateway := "172.16.0.1/30"
|
|
portal := "172.16.0.2"
|
|
dns := "0.0.0.0"
|
|
|
|
tempTun := &t.Tun{Closed: false, Limit: semaphore.NewWeighted(4)}
|
|
|
|
closer, err := t.Start(f, gateway, portal, dns)
|
|
|
|
if err != nil {
|
|
log.Errorln("startTUN error: %v", err)
|
|
tempTun.Close()
|
|
}
|
|
|
|
tempTun.Closer = closer
|
|
|
|
tun = tempTun
|
|
|
|
now := time.Now()
|
|
|
|
runTime = &now
|
|
|
|
SendMessage(Message{
|
|
Type: StartedMessage,
|
|
Data: strconv.FormatInt(runTime.UnixMilli(), 10),
|
|
})
|
|
}()
|
|
}
|
|
|
|
//export getRunTime
|
|
func getRunTime() *C.char {
|
|
if runTime == nil {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(strconv.FormatInt(runTime.UnixMilli(), 10))
|
|
}
|
|
|
|
//export stopTun
|
|
func stopTun() {
|
|
removeSocketHook()
|
|
go func() {
|
|
tunLock.Lock()
|
|
defer tunLock.Unlock()
|
|
|
|
runTime = nil
|
|
|
|
if tun != nil {
|
|
log.Errorln("[Tun] stopTun")
|
|
tun.Close()
|
|
tun = nil
|
|
}
|
|
}()
|
|
}
|
|
|
|
var errBlocked = errors.New("blocked")
|
|
|
|
//export setFdMap
|
|
func setFdMap(fd C.long) {
|
|
fdInt := int64(fd)
|
|
go func() {
|
|
fdMap.Store(fdInt)
|
|
}()
|
|
}
|
|
|
|
type Fd struct {
|
|
Id int64 `json:"id"`
|
|
Value int64 `json:"value"`
|
|
}
|
|
|
|
func markSocket(fd Fd) {
|
|
SendMessage(Message{
|
|
Type: ProtectMessage,
|
|
Data: fd,
|
|
})
|
|
}
|
|
|
|
var fdCounter int64 = 0
|
|
|
|
func initSocketHook() {
|
|
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
|
|
if platform.ShouldBlockConnection() {
|
|
return errBlocked
|
|
}
|
|
return conn.Control(func(fd uintptr) {
|
|
if tun == nil {
|
|
return
|
|
}
|
|
|
|
fdInt := int64(fd)
|
|
timeout := time.After(100 * time.Millisecond)
|
|
id := atomic.AddInt64(&fdCounter, 1)
|
|
|
|
markSocket(Fd{
|
|
Id: id,
|
|
Value: fdInt,
|
|
})
|
|
|
|
for {
|
|
select {
|
|
case <-timeout:
|
|
return
|
|
default:
|
|
exists := fdMap.Load(id)
|
|
if exists {
|
|
return
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func removeSocketHook() {
|
|
dialer.DefaultSocketHook = nil
|
|
}
|