use tun2socket instead of netstack
This commit is contained in:
16 files changed
+468
-155
No files matched your search
@@ -7,7 +7,7 @@ import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/kr328/cfa/profile"
|
||||
"github.com/kr328/cfa/config"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,12 +31,12 @@ func SetHome(homeDir string) {
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
profile.LoadDefault()
|
||||
config.LoadDefault()
|
||||
tunnel.DefaultManager.ResetStatistic()
|
||||
}
|
||||
|
||||
func SetApplicationVersion(version string) {
|
||||
profile.ApplicationVersion = version
|
||||
config.ApplicationVersion = version
|
||||
}
|
||||
|
||||
func SetLogCallback(callback LogCallback) {
|
||||
|
||||
@@ -3,40 +3,40 @@ package bridge
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kr328/cfa/profile"
|
||||
"github.com/kr328/cfa/config"
|
||||
)
|
||||
|
||||
func ResetDnsAppend(dns string) {
|
||||
if len(dns) == 0 {
|
||||
profile.NameServersAppend = make([]string, 0)
|
||||
config.NameServersAppend = make([]string, 0)
|
||||
} else {
|
||||
profile.NameServersAppend = strings.Split(dns, ",")
|
||||
config.NameServersAppend = strings.Split(dns, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func SetDnsOverrideEnabled(enabled bool) {
|
||||
if enabled {
|
||||
profile.DnsPatch = profile.OptionalDnsPatch
|
||||
config.DnsPatch = config.OptionalDnsPatch
|
||||
} else {
|
||||
profile.DnsPatch = nil
|
||||
config.DnsPatch = nil
|
||||
}
|
||||
}
|
||||
|
||||
func LoadProfileFile(path, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(profile.LoadFromFile(path, baseDir), callback)
|
||||
call(config.LoadFromFile(path, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
func DownloadProfileAndCheck(url, output, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(profile.DownloadAndCheck(url, output, baseDir), callback)
|
||||
call(config.DownloadRemote(url, output, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
func ReadProfileAndCheck(fd int, output, baseDir string, callback DoneCallback) {
|
||||
go func() {
|
||||
call(profile.ReadAndCheck(fd, output, baseDir), callback)
|
||||
call(config.DownloadLocal(fd, output, baseDir), callback)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -53,26 +53,18 @@ func StartUrlTest(group string, callback DoneCallback) {
|
||||
|
||||
p := tunnel.Proxies()[group]
|
||||
|
||||
pa, ok := p.(*outbound.Proxy)
|
||||
pi, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var providers []provider.ProxyProvider
|
||||
|
||||
switch group := pa.ProxyAdapter.(type) {
|
||||
case *outboundgroup.Fallback:
|
||||
providers = group.GetProviders()
|
||||
case *outboundgroup.URLTest:
|
||||
providers = group.GetProviders()
|
||||
case *outboundgroup.LoadBalance:
|
||||
providers = group.GetProviders()
|
||||
case *outboundgroup.Selector:
|
||||
providers = group.GetProviders()
|
||||
default:
|
||||
group, ok := pi.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
providers := group.GetProxyProviders()
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(providers))
|
||||
|
||||
@@ -91,55 +83,23 @@ func QueryAllProxyGroups(collection ProxyGroupCollection) {
|
||||
ps := tunnel.Proxies()
|
||||
|
||||
for _, p := range ps {
|
||||
pa, ok := p.(*outbound.Proxy)
|
||||
pi, ok := p.(*outbound.Proxy)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch group := pa.ProxyAdapter.(type) {
|
||||
case *outboundgroup.Fallback:
|
||||
collection.Add(
|
||||
&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: group.Now(),
|
||||
Delay: int(p.LastDelay()),
|
||||
providers: group.GetProviders(),
|
||||
},
|
||||
)
|
||||
case *outboundgroup.URLTest:
|
||||
collection.Add(
|
||||
&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: group.Now(),
|
||||
Delay: int(p.LastDelay()),
|
||||
providers: group.GetProviders(),
|
||||
},
|
||||
)
|
||||
case *outboundgroup.LoadBalance:
|
||||
collection.Add(
|
||||
&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: "",
|
||||
Delay: int(p.LastDelay()),
|
||||
providers: group.GetProviders(),
|
||||
},
|
||||
)
|
||||
case *outboundgroup.Selector:
|
||||
collection.Add(
|
||||
&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: group.Now(),
|
||||
Delay: int(p.LastDelay()),
|
||||
providers: group.GetProviders(),
|
||||
},
|
||||
)
|
||||
default:
|
||||
group, ok := pi.ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
collection.Add(&ProxyGroupItem{
|
||||
Name: group.Name(),
|
||||
Type: group.Type().String(),
|
||||
Current: group.Now(),
|
||||
Delay: 0,
|
||||
providers: group.GetProxyProviders(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ func onNewListenConfig(listen *net.ListenConfig) {
|
||||
listen.Control = onNewSocket
|
||||
}
|
||||
|
||||
func onNewSocket(network, address string, c syscall.RawConn) error {
|
||||
func onNewSocket(_, _ string, c syscall.RawConn) error {
|
||||
if cb := callback; cb != nil {
|
||||
c.Control(func(fd uintptr) {
|
||||
_ = c.Control(func(fd uintptr) {
|
||||
cb.OnCreateSocket(int(fd))
|
||||
})
|
||||
}
|
||||
@@ -38,10 +38,10 @@ func onNewSocket(network, address string, c syscall.RawConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartTunDevice(fd, mtu int, dns string, cb TunCallback) error {
|
||||
func StartTunDevice(fd, mtu int, gateway, mirror, dns string, cb TunCallback) error {
|
||||
callback = cb
|
||||
|
||||
return tun.StartTunDevice(fd, mtu, dns)
|
||||
return tun.StartTunDevice(fd, mtu, gateway, mirror, dns)
|
||||
}
|
||||
|
||||
func StopTunDevice() {
|
||||
|
||||
Submodule core/src/main/golang/clash updated: b8f879f34d...518354e7eb.
@@ -1,8 +1,9 @@
|
||||
package profile
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/kr328/cfa/utils"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -37,8 +38,8 @@ var client = &http.Client{
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
client.Close()
|
||||
server.Close()
|
||||
_ = client.Close()
|
||||
_ = server.Close()
|
||||
}()
|
||||
|
||||
return client, nil
|
||||
@@ -46,50 +47,64 @@ var client = &http.Client{
|
||||
},
|
||||
}
|
||||
|
||||
func DownloadAndCheck(url, output, baseDir string) error {
|
||||
func fetchRemote(url string) ([]byte, error) {
|
||||
request, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request.Header.Set("User-Agent", "ClashForAndroid/"+ApplicationVersion)
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
defer utils.CloseSilent(response.Body)
|
||||
|
||||
data, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return SaveAndCheck(data, output, baseDir)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func ReadAndCheck(fd int, output, baseDir string) error {
|
||||
syscall.SetNonblock(fd, true)
|
||||
func fetchLocal(fd int) ([]byte, error) {
|
||||
_ = syscall.SetNonblock(fd, true)
|
||||
|
||||
file := os.NewFile(uintptr(fd), "/dev/null")
|
||||
defer file.Close()
|
||||
defer utils.CloseSilent(file)
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
return ioutil.ReadAll(file)
|
||||
}
|
||||
|
||||
func DownloadRemote(url, output, baseDir string) error {
|
||||
data, err := fetchRemote(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return SaveAndCheck(data, output, baseDir)
|
||||
return save(data, output, baseDir)
|
||||
}
|
||||
|
||||
func SaveAndCheck(data []byte, output, baseDir string) error {
|
||||
func DownloadLocal(fd int, output, baseDir string) error {
|
||||
data, err := fetchLocal(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return save(data, output, baseDir)
|
||||
}
|
||||
|
||||
func save(data []byte, output, baseDir string) error {
|
||||
cfg, err := parseConfig(data, baseDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range cfg.Providers {
|
||||
v.Destroy()
|
||||
_ = v.Destroy()
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(output, data, defaultFileMode)
|
||||
@@ -1,7 +1,7 @@
|
||||
package profile
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/Dreamacro/clash/config"
|
||||
@@ -70,7 +70,7 @@ func LoadDefault() {
|
||||
|
||||
executor.ApplyConfig(defaultC, true)
|
||||
|
||||
tun.ResetDnsRedirect()
|
||||
tun.InitialResolver()
|
||||
}
|
||||
|
||||
// LoadFromFile - load file
|
||||
@@ -91,7 +91,7 @@ func LoadFromFile(path, baseDir string) error {
|
||||
|
||||
executor.ApplyConfig(cfg, true)
|
||||
|
||||
tun.ResetDnsRedirect()
|
||||
tun.InitialResolver()
|
||||
|
||||
log.Infoln("Profile " + path + " loaded")
|
||||
|
||||
@@ -104,13 +104,13 @@ func parseConfig(data []byte, baseDir string) (*config.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw.Experimental.Interface = ""
|
||||
raw.ExternalUI = ""
|
||||
raw.ExternalController = ""
|
||||
raw.Rule = append([]string{fmt.Sprintf("IP-CIDR,%s,REJECT,no-resolve", tunAddress)}, raw.Rule...)
|
||||
|
||||
patchRawConfig(raw)
|
||||
|
||||
if len(raw.Proxy) == 0 && len(raw.ProxyProvider) == 0 &&
|
||||
len(raw.ProxyOld) == 0 && len(raw.ProxyProviderOld) == 0 {
|
||||
return nil, errors.New("Empty Profile")
|
||||
}
|
||||
|
||||
cfg, err := config.ParseRawConfig(raw, baseDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1,6 +1,7 @@
|
||||
package profile
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/component/fakeip"
|
||||
@@ -16,6 +17,12 @@ var (
|
||||
)
|
||||
|
||||
func patchRawConfig(rawConfig *config.RawConfig) {
|
||||
rawConfig.DNS.FakeIPRange = "198.18.0.0/16"
|
||||
rawConfig.Experimental.Interface = ""
|
||||
rawConfig.ExternalUI = ""
|
||||
rawConfig.ExternalController = ""
|
||||
rawConfig.Rule = append([]string{fmt.Sprintf("IP-CIDR,%s,REJECT,no-resolve", tunAddress)}, rawConfig.Rule...)
|
||||
|
||||
if d := DnsPatch; d != nil {
|
||||
rawConfig.DNS = *d
|
||||
} else if d := OptionalDnsPatch; d != nil {
|
||||
@@ -24,13 +31,13 @@ func patchRawConfig(rawConfig *config.RawConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
if append := NameServersAppend; len(append) > 0 {
|
||||
if nameServersAppend := NameServersAppend; len(nameServersAppend) > 0 {
|
||||
d := &rawConfig.DNS
|
||||
nameservers := make([]string, len(append)+len(d.NameServer))
|
||||
copy(nameservers, append)
|
||||
copy(nameservers[len(append):], d.NameServer)
|
||||
nameServers := make([]string, len(nameServersAppend)+len(d.NameServer))
|
||||
copy(nameServers, nameServersAppend)
|
||||
copy(nameServers[len(nameServersAppend):], d.NameServer)
|
||||
|
||||
d.NameServer = nameservers
|
||||
d.NameServer = nameServers
|
||||
}
|
||||
|
||||
providers := rawConfig.ProxyProvider
|
||||
@@ -4,8 +4,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/Dreamacro/clash v0.0.0 // local
|
||||
)
|
||||
github.com/kr328/tun2socket v0.0.0-20200327164759-45012c372532 // indirect
|
||||
)
|
||||
|
||||
replace github.com/Dreamacro/clash => ./clash
|
||||
|
||||
replace github.com/google/netstack => github.com/comzyh/netstack v0.0.0-20191217044024-67c27819ada4
|
||||
@@ -1,16 +1,17 @@
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.5/go.mod h1:LSXCjyHesPY3pLjhwff1mQX72ItcBT/N2xNC685cYeU=
|
||||
github.com/comzyh/netstack v0.0.0-20191217044024-67c27819ada4/go.mod h1:jMMWEkl1smElz5KtnrIWDHxc8gtMIO/Pd8+pLyGRzT8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||
github.com/go-chi/cors v1.0.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/kr328/tun2socket v0.0.0-20200327111356-c3122f7f9742/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o=
|
||||
github.com/kr328/tun2socket v0.0.0-20200327164759-45012c372532 h1:IfYT+yTTvdxweErpymqCmjd3XKHgcyIIcQ+sftmZuuY=
|
||||
github.com/kr328/tun2socket v0.0.0-20200327164759-45012c372532/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng=
|
||||
github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -23,21 +24,20 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
"github.com/kr328/cfa/utils"
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
D "github.com/miekg/dns"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDNSReadTimeout = time.Second * 10
|
||||
)
|
||||
|
||||
var lock sync.Mutex
|
||||
var hijackAddress net.IP
|
||||
var dnsHandler dns.Handler
|
||||
|
||||
func setHijackAddress(hijackAddr net.IP) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
hijackAddress = hijackAddr
|
||||
}
|
||||
|
||||
func InitialResolver() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
rawResolver := resolver.DefaultResolver
|
||||
if rawResolver == nil {
|
||||
hijackAddress = nil
|
||||
dnsHandler = nil
|
||||
return
|
||||
}
|
||||
r, ok := rawResolver.(*dns.Resolver)
|
||||
if !ok || r == nil {
|
||||
hijackAddress = nil
|
||||
dnsHandler = nil
|
||||
return
|
||||
}
|
||||
|
||||
dnsHandler = dns.NewHandler(r)
|
||||
}
|
||||
|
||||
func hijackTCPDNS(conn net.Conn, endpoint *binding.Endpoint) bool {
|
||||
if endpoint.Target.Port != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !hijackAddress.Equal(net.IPv4zero) && !hijackAddress.Equal(net.IPv6zero) && !hijackAddress.Equal(endpoint.Target.IP) {
|
||||
return false
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer utils.CloseSilent(conn)
|
||||
|
||||
for {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(defaultDNSReadTimeout)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var length uint16
|
||||
if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
msg := &D.Msg{}
|
||||
|
||||
if err := msg.Unpack(data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
handler := dnsHandler
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
|
||||
handler(&tcpWriter{
|
||||
conn: conn,
|
||||
endpoint: endpoint,
|
||||
}, msg)
|
||||
}
|
||||
}()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func hijackDNS(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) bool {
|
||||
if endpoint.Target.Port != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !hijackAddress.Equal(net.IPv4zero) && !hijackAddress.Equal(net.IPv6zero) && !hijackAddress.Equal(endpoint.Target.IP) {
|
||||
return false
|
||||
}
|
||||
|
||||
go func() {
|
||||
msg := &D.Msg{}
|
||||
if err := msg.Unpack(payload); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
handler := dnsHandler
|
||||
handler(&udpWriter{
|
||||
endpoint: endpoint,
|
||||
sender: sender,
|
||||
}, msg)
|
||||
}()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type tcpWriter struct {
|
||||
conn net.Conn
|
||||
endpoint *binding.Endpoint
|
||||
}
|
||||
|
||||
func (r *tcpWriter) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: r.endpoint.Target.IP,
|
||||
Port: int(r.endpoint.Target.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *tcpWriter) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: r.endpoint.Source.IP,
|
||||
Port: int(r.endpoint.Source.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *tcpWriter) Write(b []byte) (int, error) {
|
||||
if len(b) > 65535 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
var length [2]byte
|
||||
binary.BigEndian.PutUint16(length[:], uint16(len(b)))
|
||||
|
||||
n, err := (&net.Buffers{length[:], b}).WriteTo(r.conn)
|
||||
|
||||
return int(n), err
|
||||
}
|
||||
|
||||
func (r *tcpWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *tcpWriter) WriteMsg(d *D.Msg) error {
|
||||
msg, err := d.Pack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Write(msg)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *tcpWriter) TsigStatus() error {
|
||||
// Unsupported
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *tcpWriter) TsigTimersOnly(bool) {
|
||||
// Unsupported
|
||||
}
|
||||
|
||||
func (r *tcpWriter) Hijack() {
|
||||
// Unsupported
|
||||
}
|
||||
|
||||
type udpWriter struct {
|
||||
endpoint *binding.Endpoint
|
||||
sender redirect.UDPSender
|
||||
}
|
||||
|
||||
func (r *udpWriter) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: r.endpoint.Target.IP,
|
||||
Port: int(r.endpoint.Target.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *udpWriter) RemoteAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: r.endpoint.Source.IP,
|
||||
Port: int(r.endpoint.Source.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *udpWriter) WriteMsg(d *D.Msg) error {
|
||||
msg, err := d.Pack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Write(msg)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *udpWriter) Write(msg []byte) (int, error) {
|
||||
ep := &binding.Endpoint{
|
||||
Source: r.endpoint.Target,
|
||||
Target: r.endpoint.Source,
|
||||
}
|
||||
|
||||
return len(msg), r.sender(msg, ep)
|
||||
}
|
||||
|
||||
func (r *udpWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *udpWriter) TsigStatus() error {
|
||||
// Unsupported
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *udpWriter) TsigTimersOnly(bool) {
|
||||
// Unsupported
|
||||
}
|
||||
|
||||
func (r *udpWriter) Hijack() {
|
||||
// Unsupported
|
||||
}
|
||||
@@ -1,36 +1,74 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/kr328/tun2socket"
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/proxy/tun"
|
||||
)
|
||||
|
||||
var tunInstance *tun.TunAdapter
|
||||
var dnsAddress string
|
||||
var adapter *tun2socket.Tun2Socket
|
||||
var mutex sync.Mutex
|
||||
|
||||
func StartTunDevice(fd, mtu int, dns string) error {
|
||||
func StartTunDevice(fd, mtu int, gateway, mirror, dnsAddress string) error {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if tunInstance != nil {
|
||||
return nil
|
||||
if adapter != nil {
|
||||
adapter.Close()
|
||||
adapter = nil
|
||||
}
|
||||
|
||||
t, err := tun.NewTunProxy("fd://" + strconv.Itoa(fd) + "?mtu=" + strconv.Itoa(mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
_ = syscall.SetNonblock(fd, true)
|
||||
|
||||
tunInstance = &t
|
||||
dnsAddress = dns
|
||||
adapter = tun2socket.NewTun2Socket(file, mtu, net.ParseIP(gateway).To4(), net.ParseIP(mirror).To4())
|
||||
|
||||
ResetDnsRedirect()
|
||||
adapter.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
|
||||
if hijackTCPDNS(conn, endpoint) {
|
||||
return
|
||||
}
|
||||
|
||||
addr := socks5.ParseAddrToSocksAddr(&net.TCPAddr{
|
||||
IP: endpoint.Target.IP,
|
||||
Port: int(endpoint.Target.Port),
|
||||
Zone: "",
|
||||
})
|
||||
|
||||
tunnel.Add(adapters.NewSocket(addr, conn, C.SOCKS, C.TCP))
|
||||
})
|
||||
adapter.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
||||
if hijackDNS(payload, endpoint, sender) {
|
||||
return
|
||||
}
|
||||
|
||||
addr := socks5.ParseAddrToSocksAddr(&net.TCPAddr{
|
||||
IP: endpoint.Target.IP,
|
||||
Port: int(endpoint.Target.Port),
|
||||
Zone: "",
|
||||
})
|
||||
pkt := &udpPacket{
|
||||
payload: payload,
|
||||
endpoint: endpoint,
|
||||
sender: sender,
|
||||
}
|
||||
|
||||
tunnel.AddPacket(adapters.NewPacket(addr, pkt, C.SOCKS))
|
||||
})
|
||||
|
||||
setHijackAddress(net.ParseIP(dnsAddress))
|
||||
InitialResolver()
|
||||
|
||||
adapter.Start()
|
||||
|
||||
log.Infoln("Android tun started")
|
||||
|
||||
@@ -41,21 +79,10 @@ func StopTunDevice() {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
t := tunInstance
|
||||
if t == nil {
|
||||
return
|
||||
if adapter != nil {
|
||||
adapter.Close()
|
||||
adapter = nil
|
||||
}
|
||||
|
||||
(*t).Close()
|
||||
tunInstance = nil
|
||||
|
||||
log.Infoln("Android tun stopped")
|
||||
}
|
||||
|
||||
func ResetDnsRedirect() {
|
||||
if tunInstance == nil {
|
||||
return
|
||||
}
|
||||
|
||||
(*tunInstance).ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), dnsAddress)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
"net"
|
||||
)
|
||||
|
||||
type udpPacket struct {
|
||||
payload []byte
|
||||
endpoint *binding.Endpoint
|
||||
sender redirect.UDPSender
|
||||
}
|
||||
|
||||
func (conn *udpPacket) Data() []byte {
|
||||
return conn.payload
|
||||
}
|
||||
|
||||
func (conn *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
if addr == nil {
|
||||
addr = &net.UDPAddr{
|
||||
IP: conn.endpoint.Target.IP,
|
||||
Port: int(conn.endpoint.Target.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, errors.New("Invalid udp address")
|
||||
}
|
||||
|
||||
ep := &binding.Endpoint{
|
||||
Source: binding.Address{
|
||||
IP: udpAddr.IP,
|
||||
Port: uint16(udpAddr.Port),
|
||||
},
|
||||
Target: conn.endpoint.Source,
|
||||
}
|
||||
|
||||
return len(b), conn.sender(b, ep)
|
||||
}
|
||||
|
||||
func (conn *udpPacket) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *udpPacket) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: conn.endpoint.Source.IP,
|
||||
Port: int(conn.endpoint.Source.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package utils
|
||||
|
||||
import "io"
|
||||
|
||||
func CloseSilent(closer io.Closer) {
|
||||
_ = closer.Close()
|
||||
}
|
||||
@@ -46,11 +46,13 @@ object Clash {
|
||||
fun startTunDevice(
|
||||
fd: Int,
|
||||
mtu: Int,
|
||||
gateway: String,
|
||||
mirror: String,
|
||||
dns: String,
|
||||
onNewSocket: (Int) -> Boolean,
|
||||
onTunStop: () -> Unit
|
||||
) {
|
||||
Bridge.startTunDevice(fd.toLong(), mtu.toLong(), dns, object : TunCallback {
|
||||
Bridge.startTunDevice(fd.toLong(), mtu.toLong(), gateway, mirror, dns, object : TunCallback {
|
||||
override fun onCreateSocket(fd: Long) {
|
||||
onNewSocket(fd.toInt())
|
||||
}
|
||||
|
||||
@@ -15,9 +15,12 @@ class TunService : VpnService(), CoroutineScope by MainScope() {
|
||||
// from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
|
||||
private const val VPN_MTU = 1500
|
||||
private const val PRIVATE_VLAN4_SUBNET = 30
|
||||
private const val PRIVATE_VLAN6_SUBNET = 126
|
||||
private const val PRIVATE_VLAN4_CLIENT = "172.31.255.253"
|
||||
private const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1"
|
||||
private const val PRIVATE_VLAN_DNS = "172.31.255.254"
|
||||
private const val PRIVATE_VLAN4_MIRROR = "172.31.255.254"
|
||||
private const val PRIVATE_VLAN6_MIRROR = "fdfe:dcba:9876::2"
|
||||
private const val PRIVATE_VLAN_DNS = "198.18.0.1"
|
||||
private const val VLAN4_ANY = "0.0.0.0"
|
||||
}
|
||||
|
||||
@@ -38,17 +41,16 @@ class TunService : VpnService(), CoroutineScope by MainScope() {
|
||||
.establish()
|
||||
?: throw NullPointerException("Unable to create VPN Service")
|
||||
|
||||
val dnsAddress =
|
||||
if (settings.get(ServiceSettings.DNS_HIJACKING))
|
||||
"$VLAN4_ANY:53"
|
||||
else
|
||||
"$PRIVATE_VLAN_DNS:53"
|
||||
val dnsAddress = if (settings.get(ServiceSettings.DNS_HIJACKING)) VLAN4_ANY else PRIVATE_VLAN_DNS
|
||||
|
||||
Log.i("TunService.startTun ${fd.fd}")
|
||||
|
||||
Clash.setDnsOverrideEnabled(settings.get(ServiceSettings.OVERRIDE_DNS))
|
||||
|
||||
Clash.startTunDevice(fd.fd, VPN_MTU, dnsAddress, this::protect, this::stopSelf)
|
||||
Clash.startTunDevice(fd.detachFd(), VPN_MTU,
|
||||
PRIVATE_VLAN4_CLIENT, PRIVATE_VLAN4_MIRROR,
|
||||
dnsAddress,
|
||||
this::protect, this::stopSelf)
|
||||
|
||||
fd.close()
|
||||
}
|
||||
@@ -182,7 +184,7 @@ class TunService : VpnService(), CoroutineScope by MainScope() {
|
||||
addAddress(PRIVATE_VLAN4_CLIENT, PRIVATE_VLAN4_SUBNET)
|
||||
|
||||
if (settings.get(ServiceSettings.IPV6_SUPPORT))
|
||||
addAddress(PRIVATE_VLAN6_CLIENT, 126)
|
||||
addAddress(PRIVATE_VLAN6_CLIENT, PRIVATE_VLAN6_SUBNET)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user