use tun2socket instead of netstack

This commit is contained in:
Kr328 committed 2020-03-28 12:30:26 +08:00
1 parent c37dc2f874
commit d42dc46b8c
16 files changed
+468 -155

No files matched your search

+3 -3
View File
@@ -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) {
+8 -8
View File
@@ -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)
}()
}
+16 -56
View File
@@ -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(),
})
}
}
+4 -4
View File
@@ -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() {
@@ -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
+2 -3
View File
@@ -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
+9 -9
View File
@@ -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=
+239
View File
@@ -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
}
+58 -31
View File
@@ -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)
}
}
+55
View File
@@ -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: "",
}
}
+7
View File
@@ -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
}