├── .github ├── env.sh └── workflows │ └── debug.yml ├── .gitignore ├── LICENSE ├── api.go ├── assets.go ├── bind.go ├── build.sh ├── certs.go ├── clash_log.go ├── comm ├── base.go ├── ipv6.go └── tun.go ├── core.go ├── crypto.go ├── debug.go ├── debug.sh ├── debug_stub.go ├── dns.go ├── errorgen └── main.go ├── errors.generated.go ├── go.mod ├── go.sum ├── gvisor ├── dispatchers.go ├── endpoint.go ├── errors.generated.go ├── gvisor.go ├── icmp.go ├── tcp.go └── udp.go ├── http.go ├── init.sh ├── io.go ├── linkname.s ├── log.go ├── nat ├── errors.generated.go ├── icmp.go ├── nat.go ├── peer.go ├── tcp.go └── udp.go ├── obfs.go ├── observatory.go ├── procfs.go ├── protect.go ├── route.go ├── ssr.go ├── stats.go ├── stun ├── errors.generated.go ├── socks.go └── stun.go ├── tun.go ├── tun └── tun.go ├── uid.go ├── url.go ├── urltest.go └── v2ray.go /.github/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -d "$ANDROID_HOME" ] || ANDROID_HOME="$ANDROID_HOME" 4 | [ -d "$ANDROID_HOME" ] || ANDROID_HOME="$HOME/Android/Sdk" 5 | [ -d "$ANDROID_HOME" ] || ANDROID_HOME="$HOME/.local/lib/android/sdk" 6 | [ -d "$ANDROID_HOME" ] || ANDROID_HOME="$HOME/Library/Android/sdk" 7 | 8 | _NDK="$ANDROID_HOME/ndk/25.0.8775105" 9 | [ -f "$_NDK/source.properties" ] || _NDK="$NDK" 10 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_NDK_HOME" 11 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_NDK_ROOT" 12 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_NDK_LATEST_HOME" 13 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_HOME/23.2.8568313" 14 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_HOME/22.1.7171670" 15 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_HOME/21.4.7075529" 16 | [ -f "$_NDK/source.properties" ] || _NDK="$ANDROID_HOME/ndk-bundle" 17 | 18 | if [ ! -f "$_NDK/source.properties" ]; then 19 | echo "Error: NDK not found." 20 | exit 1 21 | fi 22 | 23 | export ANDROID_HOME 24 | export ANDROID_NDK_HOME=$_NDK 25 | export NDK=$_NDK 26 | 27 | if [ ! $(command -v go) ]; then 28 | if [ -d /usr/lib/go ]; then 29 | export PATH="$PATH:/usr/lib/go/bin" 30 | elif [ /usr/lib/go-1.17 ]; then 31 | export PATH="$PATH:/usr/lib/go-1.17/bin" 32 | elif [ -d $HOME/.go ]; then 33 | export PATH="$PATH:$HOME/.go/bin" 34 | fi 35 | fi 36 | 37 | if [ $(command -v go) ]; then 38 | export PATH="$PATH:$(go env GOPATH)/bin" 39 | fi -------------------------------------------------------------------------------- /.github/workflows/debug.yml: -------------------------------------------------------------------------------- 1 | name: Debug build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | name: Debug build 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | - name: Get latest go version 21 | id: version 22 | run: | 23 | echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') 24 | - name: Setup Go 25 | uses: actions/setup-go@v2 26 | with: 27 | go-version: ${{ steps.version.outputs.go_version }} 28 | - name: Init 29 | run: ./init.sh 30 | - name: Build 31 | run: ./build.sh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pb.go 2 | binary*.go 3 | *.[a|j]ar 4 | .idea/ 5 | /build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2021 by nekohasekai 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/sagernet/sagerconnect/api" 10 | "github.com/sirupsen/logrus" 11 | "libcore/comm" 12 | ) 13 | 14 | type ApiInstance struct { 15 | access sync.Mutex 16 | deviceName string 17 | socksPort int32 18 | dnsPort int32 19 | debug bool 20 | bypassLan bool 21 | 22 | conn *net.UDPConn 23 | started bool 24 | } 25 | 26 | func NewApiInstance(deviceName string, socksPort int32, dnsPort int32, debug bool, bypassLan bool) *ApiInstance { 27 | return &ApiInstance{ 28 | deviceName: deviceName, 29 | socksPort: socksPort, 30 | dnsPort: dnsPort, 31 | debug: debug, 32 | bypassLan: bypassLan, 33 | } 34 | } 35 | 36 | func (i *ApiInstance) Start() (err error) { 37 | i.access.Lock() 38 | defer i.access.Unlock() 39 | 40 | if i.started { 41 | return errors.New("already started") 42 | } 43 | 44 | i.conn, err = net.ListenUDP("udp4", &net.UDPAddr{ 45 | IP: net.IPv4zero, 46 | Port: 11451, 47 | }) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | i.started = true 53 | go i.loop() 54 | 55 | return nil 56 | } 57 | 58 | func (i *ApiInstance) Close() { 59 | i.access.Lock() 60 | defer i.access.Unlock() 61 | 62 | if i.started { 63 | i.started = false 64 | comm.CloseIgnore(i.conn) 65 | } 66 | } 67 | 68 | func (i *ApiInstance) loop() { 69 | buffer := make([]byte, 2048) 70 | for i.started { 71 | length, addr, err := i.conn.ReadFrom(buffer) 72 | if err != nil { 73 | continue 74 | } 75 | query, err := api.ParseQuery(buffer[:length]) 76 | if err != nil { 77 | if err != nil && strings.Contains(err.Error(), "upgrade") { 78 | message, err := api.MakeResponse(&api.Response{Version: api.Version, DeviceName: "", SocksPort: 0, DnsPort: 0, Debug: false, BypassLan: false}) 79 | if err != nil { 80 | logrus.Warnf("api: make response error: %v", err) 81 | continue 82 | } 83 | 84 | _, err = i.conn.WriteTo(message, addr) 85 | if err != nil { 86 | logrus.Warnf("api: send response error: %v", err) 87 | continue 88 | } 89 | 90 | } 91 | logrus.Warnf("api: parse error: %v", err) 92 | continue 93 | } 94 | 95 | logrus.Infof("api: new query from %s (%s)", query.DeviceName, addr.String()) 96 | 97 | response := api.Response{Version: api.Version, DeviceName: i.deviceName, SocksPort: uint16(i.socksPort), DnsPort: uint16(i.dnsPort), Debug: i.debug, BypassLan: i.bypassLan} 98 | message, err := api.MakeResponse(&response) 99 | if err != nil { 100 | logrus.Warnf("api: make response error: %v", err) 101 | continue 102 | } 103 | 104 | _, err = i.conn.WriteTo(message, addr) 105 | if err != nil { 106 | logrus.Warnf("api: send response error: %v", err) 107 | continue 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /assets.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "sync" 10 | 11 | "github.com/sagernet/gomobile/asset" 12 | "github.com/sirupsen/logrus" 13 | "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" 14 | "libcore/comm" 15 | ) 16 | 17 | const ( 18 | geoipDat = "geoip.dat" 19 | geositeDat = "geosite.dat" 20 | browserForwarder = "index.js" 21 | geoipVersion = "geoip.version.txt" 22 | geositeVersion = "geosite.version.txt" 23 | coreVersion = "core.version.txt" 24 | mozillaIncludedPem = "mozilla_included.pem" 25 | ) 26 | 27 | var ( 28 | assetsPrefix string 29 | internalAssetsPath string 30 | externalAssetsPath string 31 | ) 32 | 33 | var ( 34 | useOfficialAssets bool 35 | extracted map[string]bool 36 | assetsAccess *sync.Mutex 37 | ) 38 | 39 | type Func interface { 40 | Invoke() error 41 | } 42 | 43 | type BoolFunc interface { 44 | Invoke() bool 45 | } 46 | 47 | func InitializeV2Ray(internalAssets string, externalAssets string, prefix string, useOfficial BoolFunc, useSystemCerts BoolFunc, skipExtract bool) error { 48 | assetsAccess = new(sync.Mutex) 49 | assetsAccess.Lock() 50 | extracted = make(map[string]bool) 51 | 52 | assetsPrefix = prefix 53 | internalAssetsPath = internalAssets 54 | externalAssetsPath = externalAssets 55 | 56 | filesystem.NewFileSeeker = func(path string) (io.ReadSeekCloser, error) { 57 | _, fileName := filepath.Split(path) 58 | 59 | if !extracted[fileName] { 60 | assetsAccess.Lock() 61 | assetsAccess.Unlock() 62 | } 63 | 64 | paths := []string{ 65 | internalAssetsPath + fileName, 66 | externalAssetsPath + fileName, 67 | } 68 | 69 | var err error 70 | 71 | for _, path = range paths { 72 | _, err = os.Stat(path) 73 | if err == nil { 74 | return os.Open(path) 75 | } 76 | } 77 | 78 | file, err := asset.Open(assetsPrefix + fileName) 79 | if err == nil { 80 | extracted[fileName] = true 81 | return file, nil 82 | } 83 | 84 | err = extractAssetName(fileName, false) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | for _, path = range paths { 90 | _, err = os.Stat(path) 91 | if err == nil { 92 | return os.Open(path) 93 | } 94 | if !os.IsNotExist(err) { 95 | return nil, err 96 | } 97 | } 98 | 99 | return nil, err 100 | } 101 | 102 | filesystem.NewFileReader = func(path string) (io.ReadCloser, error) { 103 | return filesystem.NewFileSeeker(path) 104 | } 105 | 106 | if skipExtract { 107 | assetsAccess.Unlock() 108 | return nil 109 | } 110 | 111 | extract := func(name string) { 112 | err := extractAssetName(name, false) 113 | if err != nil { 114 | logrus.Warnf("Extract %s failed: %v", geoipDat, err) 115 | } else { 116 | extracted[name] = true 117 | } 118 | } 119 | 120 | go func() { 121 | defer assetsAccess.Unlock() 122 | useOfficialAssets = useOfficial.Invoke() 123 | 124 | extract(geoipDat) 125 | extract(geositeDat) 126 | extract(browserForwarder) 127 | 128 | err := extractRootCACertsPem() 129 | if err != nil { 130 | logrus.Warn(newError("failed to extract root ca certs from assets").Base(err)) 131 | return 132 | } 133 | 134 | UpdateSystemRoots(useSystemCerts.Invoke()) 135 | }() 136 | 137 | return nil 138 | } 139 | 140 | func extractAssetName(name string, force bool) error { 141 | var dir string 142 | if name == browserForwarder { 143 | dir = internalAssetsPath 144 | } else { 145 | dir = externalAssetsPath 146 | } 147 | var version string 148 | switch name { 149 | case geoipDat: 150 | version = geoipVersion 151 | case geositeDat: 152 | version = geositeVersion 153 | case browserForwarder: 154 | version = coreVersion 155 | } 156 | 157 | var localVersion string 158 | var assetVersion string 159 | 160 | loadAssetVersion := func() error { 161 | av, err := asset.Open(assetsPrefix + version) 162 | if err != nil { 163 | return newError("open version in assets").Base(err) 164 | } 165 | b, err := ioutil.ReadAll(av) 166 | comm.CloseIgnore(av) 167 | if err != nil { 168 | return newError("read internal version").Base(err) 169 | } 170 | assetVersion = string(b) 171 | return nil 172 | } 173 | 174 | doExtract := false 175 | 176 | // check version 177 | 178 | if _, versionNotFoundError := os.Stat(dir + version); versionNotFoundError != nil { 179 | _, assetNotFoundError := os.Stat(dir + name) 180 | doExtract = assetNotFoundError != nil || force 181 | } else if useOfficialAssets { 182 | b, err := ioutil.ReadFile(dir + version) 183 | if err != nil { 184 | doExtract = true 185 | _ = os.RemoveAll(version) 186 | } else { 187 | localVersion = string(b) 188 | err = loadAssetVersion() 189 | if err != nil { 190 | return err 191 | } 192 | av, err := strconv.ParseUint(assetVersion, 10, 64) 193 | if err != nil { 194 | doExtract = assetVersion != localVersion || force 195 | } else { 196 | lv, err := strconv.ParseUint(localVersion, 10, 64) 197 | doExtract = err != nil || av > lv || force 198 | } 199 | } 200 | } else { 201 | doExtract = force 202 | } 203 | 204 | if doExtract { 205 | if assetVersion == "" { 206 | err := loadAssetVersion() 207 | if err != nil { 208 | return err 209 | } 210 | } 211 | } else { 212 | return nil 213 | } 214 | 215 | err := extractAsset(assetsPrefix+name+".xz", dir+name) 216 | if err == nil { 217 | err = unxz(dir + name) 218 | } 219 | if err != nil { 220 | return err 221 | } 222 | 223 | o, err := os.Create(dir + version) 224 | if err != nil { 225 | return err 226 | } 227 | _, err = io.WriteString(o, assetVersion) 228 | comm.CloseIgnore(o) 229 | return err 230 | } 231 | 232 | func extractRootCACertsPem() error { 233 | path := internalAssetsPath + mozillaIncludedPem 234 | sumPath := path + ".sha256sum" 235 | sumInternal, err := asset.Open(mozillaIncludedPem + ".sha256sum") 236 | if err != nil { 237 | return newError("open pem version in assets").Base(err) 238 | } 239 | defer sumInternal.Close() 240 | sumBytes, err := ioutil.ReadAll(sumInternal) 241 | if err != nil { 242 | return newError("read internal version").Base(err) 243 | } 244 | _, pemSha256sumNotExists := os.Stat(sumPath) 245 | if pemSha256sumNotExists == nil { 246 | sumExternal, err := ioutil.ReadFile(sumPath) 247 | if err == nil { 248 | if string(sumBytes) == string(sumExternal) { 249 | return nil 250 | } 251 | } 252 | } 253 | pemFile, err := os.Create(path) 254 | if err != nil { 255 | return newError("create pem file").Base(err) 256 | } 257 | defer pemFile.Close() 258 | pem, err := asset.Open(mozillaIncludedPem) 259 | if err != nil { 260 | return newError("open pem in assets").Base(err) 261 | } 262 | defer pem.Close() 263 | _, err = io.Copy(pemFile, pem) 264 | if err != nil { 265 | return newError("write pem file") 266 | } 267 | return ioutil.WriteFile(sumPath, sumBytes, 0o644) 268 | } 269 | 270 | func extractAsset(assetPath string, path string) error { 271 | i, err := asset.Open(assetPath) 272 | if err != nil { 273 | return err 274 | } 275 | defer comm.CloseIgnore(i) 276 | o, err := os.Create(path) 277 | if err != nil { 278 | return err 279 | } 280 | defer comm.CloseIgnore(o) 281 | _, err = io.Copy(o, i) 282 | if err == nil { 283 | logrus.Debugf("Extract >> %s", path) 284 | } 285 | return err 286 | } 287 | -------------------------------------------------------------------------------- /bind.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/sirupsen/logrus" 7 | ) 8 | 9 | var upstreamNetworkName string 10 | 11 | func bindToUpstream(fd uintptr) { 12 | if upstreamNetworkName == "" { 13 | logrus.Warn("empty upstream network name") 14 | return 15 | } 16 | err := syscall.BindToDevice(int(fd), upstreamNetworkName) 17 | if err != nil { 18 | logrus.Warn("failed to bind socket to upstream network ", upstreamNetworkName, ": ", err) 19 | } 20 | } 21 | 22 | func BindNetworkName(name string) { 23 | if name != upstreamNetworkName { 24 | upstreamNetworkName = name 25 | logrus.Debug("updated upstream network name: ", upstreamNetworkName) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .github/env.sh 4 | 5 | BUILD="../libcore_build" 6 | 7 | rm -rf $BUILD/android \ 8 | $BUILD/java \ 9 | $BUILD/javac-output \ 10 | $BUILD/src* 11 | 12 | gomobile bind -v -cache $(realpath $BUILD) -androidapi 21 -trimpath -tags='disable_debug' -ldflags='-s -w -buildid=' . || exit 1 13 | rm -r libcore-sources.jar 14 | 15 | proj=../SagerNet/app/libs 16 | if [ -d $proj ]; then 17 | cp -f libcore.aar $proj 18 | echo ">> install $(realpath $proj)/libcore.aar" 19 | fi 20 | -------------------------------------------------------------------------------- /certs.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "crypto/x509" 5 | "io/ioutil" 6 | _ "unsafe" 7 | 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | //go:linkname systemRoots crypto/x509.systemRoots 12 | var systemRoots *x509.CertPool 13 | 14 | func updateRootCACerts() { 15 | x509.SystemCertPool() 16 | roots := x509.NewCertPool() 17 | pemFile, err := ioutil.ReadFile(internalAssetsPath + mozillaIncludedPem) 18 | if err != nil { 19 | logrus.Warn("failed to load root ca certificates from internal assets dir: ", err) 20 | return 21 | } 22 | if !roots.AppendCertsFromPEM(pemFile) { 23 | logrus.Warn("failed to append certificates from pem") 24 | return 25 | } 26 | systemRoots = roots 27 | logrus.Info("updated root ca certificate list") 28 | } 29 | 30 | //go:linkname initSystemRoots crypto/x509.initSystemRoots 31 | func initSystemRoots() 32 | 33 | var disableSystem bool 34 | 35 | func UpdateSystemRoots(useSystem bool) { 36 | if disableSystem != useSystem { 37 | return 38 | } 39 | disableSystem = !disableSystem 40 | 41 | if useSystem { 42 | initSystemRoots() 43 | logrus.Info("reset systemRoots") 44 | } else { 45 | updateRootCACerts() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /clash_log.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import _ "unsafe" 4 | 5 | //go:linkname clashLogCh github.com/Dreamacro/clash/log.logCh 6 | var clashLogCh chan interface{} 7 | 8 | func init() { 9 | close(clashLogCh) 10 | } 11 | -------------------------------------------------------------------------------- /comm/base.go: -------------------------------------------------------------------------------- 1 | package comm 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common" 7 | ) 8 | 9 | type closerWrapper struct { 10 | closer func() 11 | } 12 | 13 | func (c closerWrapper) Close() error { 14 | c.closer() 15 | return nil 16 | } 17 | 18 | func Closer(closer func()) io.Closer { 19 | return closerWrapper{closer} 20 | } 21 | 22 | func CloseIgnore(closer ...interface{}) { 23 | for _, c := range closer { 24 | if c == nil { 25 | continue 26 | } 27 | if ia, ok := c.(common.Interruptible); ok { 28 | ia.Interrupt() 29 | } else if ca, ok := c.(common.Closable); ok { 30 | _ = ca.Close() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /comm/ipv6.go: -------------------------------------------------------------------------------- 1 | package comm 2 | 3 | const ( 4 | IPv6Disable = iota 5 | IPv6Enable 6 | IPv6Prefer 7 | IPv6Only 8 | ) 9 | -------------------------------------------------------------------------------- /comm/tun.go: -------------------------------------------------------------------------------- 1 | package comm 2 | 3 | const ( 4 | TunImplementationGVisor = iota 5 | TunImplementationSystem 6 | ) 7 | -------------------------------------------------------------------------------- /core.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sagernet/libping" 7 | "github.com/v2fly/v2ray-core/v5/common/net" 8 | "libcore/stun" 9 | ) 10 | 11 | //go:generate go run ./errorgen 12 | 13 | func Setenv(key, value string) error { 14 | return os.Setenv(key, value) 15 | } 16 | 17 | func Unsetenv(key string) error { 18 | return os.Unsetenv(key) 19 | } 20 | 21 | func IcmpPing(address string, timeout int32) (int32, error) { 22 | return libping.IcmpPing(address, timeout) 23 | } 24 | 25 | const ( 26 | StunNoResult int32 = iota 27 | StunEndpointIndependentNoNAT 28 | StunEndpointIndependent 29 | StunAddressDependent 30 | StunAddressAndPortDependent 31 | ) 32 | 33 | type StunResult struct { 34 | NatMapping int32 35 | NatFiltering int32 36 | } 37 | 38 | func StunTest(serverAddress string, socksPort int32) (*StunResult, error) { 39 | natMapping, natFiltering, err := stun.Test(serverAddress, int(socksPort)) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return &StunResult{ 44 | NatMapping: int32(natMapping), 45 | NatFiltering: int32(natFiltering), 46 | }, nil 47 | } 48 | 49 | func EnableConnectionPool() { 50 | net.EnableConnectionPool() 51 | } 52 | 53 | func DisableConnectionPool() { 54 | net.DisableConnectionPool() 55 | } 56 | 57 | func ResetConnections() { 58 | net.ResetConnections() 59 | } 60 | -------------------------------------------------------------------------------- /crypto.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "crypto/sha1" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | ) 8 | 9 | func Sha1(data []byte) []byte { 10 | sum := sha1.Sum(data) 11 | return sum[:] 12 | } 13 | 14 | func Sha256Hex(data []byte) string { 15 | sum := sha256.Sum256(data) 16 | return hex.EncodeToString(sum[:]) 17 | } 18 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | //go:build !disable_debug 2 | 3 | package libcore 4 | 5 | import ( 6 | "net/http" 7 | _ "net/http/pprof" 8 | 9 | "libcore/comm" 10 | ) 11 | 12 | type DebugInstance struct { 13 | server *http.Server 14 | } 15 | 16 | func NewDebugInstance() *DebugInstance { 17 | s := &http.Server{ 18 | Addr: "0.0.0.0:8964", 19 | } 20 | go func() { 21 | _ = s.ListenAndServe() 22 | }() 23 | return &DebugInstance{s} 24 | } 25 | 26 | func (d *DebugInstance) Close() { 27 | comm.CloseIgnore(d.server) 28 | } 29 | -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .github/env.sh 4 | 5 | BUILD="../libcore_build_debug" 6 | 7 | rm -rf $BUILD/android \ 8 | $BUILD/java \ 9 | $BUILD/javac-output \ 10 | $BUILD/src* 11 | 12 | gomobile bind -v -cache $(realpath $BUILD) -androidapi 21 . || exit 1 13 | rm -r libcore-sources.jar 14 | 15 | proj=../SagerNet/app/libs 16 | if [ -d $proj ]; then 17 | cp -f libcore.aar $proj 18 | echo ">> install $(realpath $proj)/libcore.aar" 19 | fi 20 | -------------------------------------------------------------------------------- /debug_stub.go: -------------------------------------------------------------------------------- 1 | //go:build disable_debug 2 | 3 | package libcore 4 | 5 | type DebugInstance struct{} 6 | 7 | func NewDebugInstance() *DebugInstance { 8 | return new(DebugInstance) 9 | } 10 | 11 | func (*DebugInstance) Close() { 12 | } 13 | -------------------------------------------------------------------------------- /dns.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/netip" 7 | "strings" 8 | "syscall" 9 | 10 | "github.com/v2fly/v2ray-core/v5/common" 11 | "github.com/v2fly/v2ray-core/v5/common/buf" 12 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 13 | "github.com/v2fly/v2ray-core/v5/common/session" 14 | "github.com/v2fly/v2ray-core/v5/common/task" 15 | "github.com/v2fly/v2ray-core/v5/features/dns" 16 | "github.com/v2fly/v2ray-core/v5/features/dns/localdns" 17 | "golang.org/x/net/dns/dnsmessage" 18 | "libcore/comm" 19 | ) 20 | 21 | type QueryContext struct { 22 | ctx context.Context 23 | message []byte 24 | ips []net.IP 25 | error error 26 | } 27 | 28 | func (c *QueryContext) OnCancel(callback Func) { 29 | go func() { 30 | <-c.ctx.Done() 31 | callback.Invoke() 32 | }() 33 | } 34 | 35 | func (c *QueryContext) Success(result string) { 36 | c.ips = common.Map(common.Filter(strings.Split(result, "\n"), func(it string) bool { 37 | return common.IsNotBlank(it) 38 | }), func(it string) net.IP { 39 | return net.ParseIP(it) 40 | }) 41 | } 42 | 43 | func (c *QueryContext) RawSuccess(result []byte) { 44 | c.message = make([]byte, len(result)) 45 | copy(c.message, result) 46 | } 47 | 48 | func (c *QueryContext) ErrorCode(code int32) { 49 | c.error = dns.RCodeError(code) 50 | } 51 | 52 | func (c *QueryContext) Errno(errno int32) { 53 | c.error = syscall.Errno(errno) 54 | } 55 | 56 | type LocalResolver interface { 57 | HasRawSupport() bool 58 | QueryRaw(ctx *QueryContext, message []byte) error 59 | LookupIP(ctx *QueryContext, network string, domain string) error 60 | } 61 | 62 | var _ localdns.LocalTransport = (*localTransport)(nil) 63 | 64 | type localTransport struct { 65 | r LocalResolver 66 | } 67 | 68 | func (l *localTransport) Type() dns.TransportType { 69 | if l.r.HasRawSupport() { 70 | return dns.TransportTypeExchangeRaw 71 | } else { 72 | return dns.TransportTypeLookup 73 | } 74 | } 75 | 76 | func (l *localTransport) Write(ctx context.Context, message *dnsmessage.Message) error { 77 | return common.ErrNoClue 78 | } 79 | 80 | func (l *localTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { 81 | return nil, common.ErrNoClue 82 | } 83 | 84 | func (l *localTransport) ExchangeRaw(ctx context.Context, message *buf.Buffer) (*buf.Buffer, error) { 85 | query := &QueryContext{ 86 | ctx: ctx, 87 | } 88 | var response *buf.Buffer 89 | return response, task.Run(ctx, func() error { 90 | err := l.r.QueryRaw(query, message.Bytes()) 91 | if err != nil { 92 | return err 93 | } 94 | if query.error != nil { 95 | return query.error 96 | } 97 | response = buf.FromBytes(query.message) 98 | return nil 99 | }) 100 | } 101 | 102 | func (l *localTransport) Lookup(ctx context.Context, domain string, strategy dns.QueryStrategy) ([]net.IP, error) { 103 | var network string 104 | switch strategy { 105 | case dns.QueryStrategy_USE_IP4: 106 | network = "ip4" 107 | case dns.QueryStrategy_USE_IP6: 108 | network = "ip6" 109 | default: 110 | network = "ip" 111 | } 112 | query := &QueryContext{ 113 | ctx: ctx, 114 | } 115 | var response []net.IP 116 | return response, task.Run(ctx, func() error { 117 | err := l.r.LookupIP(query, network, domain) 118 | if err != nil { 119 | return err 120 | } 121 | if query.error != nil { 122 | return query.error 123 | } 124 | response = query.ips 125 | if len(response) == 0 { 126 | return dns.ErrEmptyResponse 127 | } 128 | return nil 129 | }) 130 | } 131 | 132 | func (l *localTransport) IsLocalTransport() { 133 | } 134 | 135 | func (l *localTransport) Close() error { 136 | return nil 137 | } 138 | 139 | func SetLocalhostResolver(local LocalResolver) { 140 | if local == nil { 141 | localdns.SetTransport(nil) 142 | } else { 143 | localdns.SetTransport(&localTransport{ 144 | local, 145 | }) 146 | } 147 | } 148 | 149 | func init() { 150 | SetCurrentDomainNameSystemQueryInstance(nil) 151 | } 152 | 153 | var dnsAddress = v2rayNet.IPAddress([]byte{1, 0, 0, 1}) 154 | 155 | func SetCurrentDomainNameSystemQueryInstance(instance *V2RayInstance) { 156 | if instance == nil { 157 | net.DefaultResolver = &net.Resolver{ 158 | PreferGo: false, 159 | } 160 | } else { 161 | net.DefaultResolver = &net.Resolver{ 162 | PreferGo: true, 163 | Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 164 | conn, err := instance.dialContext(session.ContextWithInbound(ctx, &session.Inbound{ 165 | Tag: "dns-in", 166 | }), v2rayNet.Destination{ 167 | Network: v2rayNet.Network_UDP, 168 | Address: dnsAddress, 169 | Port: 53, 170 | }) 171 | if err == nil { 172 | conn = &pinnedPacketConn{conn} 173 | } 174 | return conn, err 175 | }, 176 | } 177 | } 178 | } 179 | 180 | type pinnedPacketConn struct { 181 | net.Conn 182 | } 183 | 184 | func (c *pinnedPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 185 | n, err = c.Conn.Read(p) 186 | if err == nil { 187 | addr = c.Conn.RemoteAddr() 188 | } 189 | return 190 | } 191 | 192 | func (c *pinnedPacketConn) WriteTo(p []byte, _ net.Addr) (n int, err error) { 193 | return c.Conn.Write(p) 194 | } 195 | 196 | func EncodeDomainNameSystemQuery(id int32, domain string, ipv6Mode int32) ([]byte, error) { 197 | if !strings.HasSuffix(domain, ".") { 198 | domain = domain + "." 199 | } 200 | name, err := dnsmessage.NewName(domain) 201 | if err != nil { 202 | return nil, newError("domain name too long").Base(err) 203 | } 204 | message := new(dnsmessage.Message) 205 | message.Header.ID = uint16(id) 206 | message.Header.RecursionDesired = true 207 | if ipv6Mode != comm.IPv6Only { 208 | message.Questions = append(message.Questions, dnsmessage.Question{ 209 | Name: name, 210 | Type: dnsmessage.TypeA, 211 | Class: dnsmessage.ClassINET, 212 | }) 213 | } 214 | if ipv6Mode != comm.IPv6Disable { 215 | message.Questions = append(message.Questions, dnsmessage.Question{ 216 | Name: name, 217 | Type: dnsmessage.TypeAAAA, 218 | Class: dnsmessage.ClassINET, 219 | }) 220 | } 221 | return message.Pack() 222 | } 223 | 224 | func DecodeContentDomainNameSystemResponse(content []byte) (response string, err error) { 225 | var ( 226 | header dnsmessage.Header 227 | answerHeader dnsmessage.ResourceHeader 228 | aAnswer dnsmessage.AResource 229 | aaaaAnswer dnsmessage.AAAAResource 230 | ) 231 | parser := new(dnsmessage.Parser) 232 | if header, err = parser.Start(content); err != nil { 233 | err = newError("failed to parse DNS response").Base(err) 234 | return 235 | } 236 | if header.RCode != dnsmessage.RCodeSuccess { 237 | return "", newError("rcode: ", header.RCode.String()) 238 | } 239 | if err = parser.SkipAllQuestions(); err != nil { 240 | err = newError("failed to skip questions in DNS response").Base(err) 241 | return 242 | } 243 | for { 244 | answerHeader, err = parser.AnswerHeader() 245 | if err != nil { 246 | if err != dnsmessage.ErrSectionDone { 247 | err = newError("failed to parse answer section for domain: ", answerHeader.Name.String()).Base(err) 248 | } else { 249 | err = nil 250 | } 251 | break 252 | } 253 | 254 | switch answerHeader.Type { 255 | case dnsmessage.TypeA: 256 | aAnswer, err = parser.AResource() 257 | if err != nil { 258 | err = newError("failed to parse A record for domain: ", answerHeader.Name).Base(err) 259 | return 260 | } 261 | response += " " + netip.AddrFrom4(aAnswer.A).String() 262 | case dnsmessage.TypeAAAA: 263 | aaaaAnswer, err = parser.AAAAResource() 264 | if err != nil { 265 | err = newError("failed to parse AAAA record for domain: ", answerHeader.Name).Base(err) 266 | return 267 | } 268 | response += " " + netip.AddrFrom16(aaaaAnswer.AAAA).String() 269 | default: 270 | if err = parser.SkipAnswer(); err != nil { 271 | err = newError("failed to skip answer").Base(err) 272 | return 273 | } 274 | continue 275 | } 276 | } 277 | return 278 | } 279 | -------------------------------------------------------------------------------- /errorgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func main() { 10 | pwd, err := os.Getwd() 11 | if err != nil { 12 | fmt.Println("can not get current working directory") 13 | os.Exit(1) 14 | } 15 | pkg := filepath.Base(pwd) 16 | if pkg == "v2ray-core" { 17 | pkg = "core" 18 | } 19 | 20 | file, err := os.OpenFile("errors.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644) 21 | if err != nil { 22 | fmt.Printf("Failed to generate errors.generated.go: %v", err) 23 | os.Exit(1) 24 | } 25 | defer file.Close() 26 | 27 | fmt.Fprintf(file, `package %s 28 | 29 | import ( 30 | "fmt" 31 | "github.com/v2fly/v2ray-core/v5/common/errors" 32 | ) 33 | 34 | type errPathObjHolder struct{} 35 | 36 | func newError(values ...interface{}) *errors.Error { 37 | return errors.New(values...).WithPathObj(errPathObjHolder{}) 38 | } 39 | 40 | func newErrorf(format string, a ...interface{}) *errors.Error { 41 | return errors.New(fmt.Sprintf(format, a)).WithPathObj(errPathObjHolder{}) 42 | } 43 | 44 | `, pkg) 45 | } 46 | -------------------------------------------------------------------------------- /errors.generated.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/errors" 7 | ) 8 | 9 | type errPathObjHolder struct{} 10 | 11 | func newError(values ...interface{}) *errors.Error { 12 | return errors.New(values...).WithPathObj(errPathObjHolder{}) 13 | } 14 | 15 | func newErrorf(format string, a ...interface{}) *errors.Error { 16 | return errors.New(fmt.Sprintf(format, a)).WithPathObj(errPathObjHolder{}) 17 | } 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module libcore 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/Dreamacro/clash v1.11.4 7 | github.com/golang/protobuf v1.5.2 8 | github.com/pion/stun v0.3.6-0.20211201014640-159901e761c9 9 | github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca 10 | github.com/sagernet/libping v0.1.1 11 | github.com/sagernet/sagerconnect v0.1.7 12 | github.com/sirupsen/logrus v1.9.0 13 | github.com/ulikunitz/xz v0.5.10 14 | github.com/v2fly/v2ray-core/v5 v5.0.7 15 | golang.org/x/net v0.7.0 16 | golang.org/x/sys v0.5.0 17 | gvisor.dev/gvisor v0.0.0 18 | ) 19 | 20 | // https://github.com/google/gvisor/releases/tag/release-20211129.0 21 | //replace gvisor.dev/gvisor => ../gvisor 22 | replace gvisor.dev/gvisor => github.com/sagernet/gvisor v0.0.0-20220402114650-763d12dc953e 23 | 24 | //replace github.com/v2fly/v2ray-core/v5 => ../v2ray-core 25 | 26 | replace github.com/v2fly/v2ray-core/v5 => github.com/sagernet/v2ray-core/v5 v5.0.17-0.20220728040302-f7e556acfff3 27 | 28 | //replace github.com/sagernet/sing => ../sing 29 | 30 | require ( 31 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect 32 | github.com/cheekybits/genny v1.0.0 // indirect 33 | github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect 34 | github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb // indirect 35 | github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect 36 | github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 // indirect 37 | github.com/fsnotify/fsnotify v1.5.1 // indirect 38 | github.com/geeksbaek/seed v0.0.0-20180909040025-2a7f5fb92e22 // indirect 39 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect 40 | github.com/google/btree v1.0.1 // indirect 41 | github.com/gorilla/websocket v1.5.0 // indirect 42 | github.com/jhump/protoreflect v1.12.0 // indirect 43 | github.com/kierdavis/cfb8 v0.0.0-20180105024805-3a17c36ee2f8 // indirect 44 | github.com/klauspost/cpuid/v2 v2.0.12 // indirect 45 | github.com/lucas-clemente/quic-go v0.28.1 // indirect 46 | github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect 47 | github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect 48 | github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect 49 | github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect 50 | github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect 51 | github.com/nxadm/tail v1.4.8 // indirect 52 | github.com/onsi/ginkgo v1.16.5 // indirect 53 | github.com/pires/go-proxyproto v0.6.2 // indirect 54 | github.com/pkg/errors v0.9.1 // indirect 55 | github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect 56 | github.com/sagernet/sing v0.0.0-20220726034811-bc109486f14e // indirect 57 | github.com/sagernet/sing-shadowsocks v0.0.0-20220726034922-ebbaadcae06b // indirect 58 | github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect 59 | github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 // indirect 60 | github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect 61 | github.com/xtaci/smux v1.5.16 // indirect 62 | github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect 63 | go.starlark.net v0.0.0-20220714194419-4cadf0a12139 // indirect 64 | go4.org/intern v0.0.0-20220301175310-a089fc204883 // indirect 65 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect 66 | golang.org/x/crypto v0.1.0 // indirect 67 | golang.org/x/mod v0.6.0 // indirect 68 | golang.org/x/text v0.7.0 // indirect 69 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect 70 | golang.org/x/tools v0.2.0 // indirect 71 | golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect 72 | golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 // indirect 73 | google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect 74 | google.golang.org/grpc v1.48.0 // indirect 75 | google.golang.org/protobuf v1.28.0 // indirect 76 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 77 | inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect 78 | lukechampine.com/blake3 v1.1.7 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /gvisor/dispatchers.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/unix" 7 | "gvisor.dev/gvisor/pkg/tcpip" 8 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 9 | "gvisor.dev/gvisor/pkg/tcpip/header" 10 | "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" 11 | "gvisor.dev/gvisor/pkg/tcpip/stack" 12 | ) 13 | 14 | // bufConfig defines the shape of the vectorised view used to read packets from the NIC. 15 | var bufConfig = []int{128, 256, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768} 16 | 17 | type iovecBuffer struct { 18 | // views are the actual buffers that hold the packet contents. 19 | views []buffer.View 20 | 21 | // iovecs are initialized with base pointers/len of the corresponding 22 | // entries in the views defined above, except when GSO is enabled 23 | // (skipsVnetHdr) then the first iovec points to a buffer for the vnet header 24 | // which is stripped before the views are passed up the stack for further 25 | // processing. 26 | iovecs []unix.Iovec 27 | 28 | // sizes is an array of buffer sizes for the underlying views. sizes is 29 | // immutable. 30 | sizes []int 31 | } 32 | 33 | func newIovecBuffer(sizes []int) *iovecBuffer { 34 | b := &iovecBuffer{ 35 | views: make([]buffer.View, len(sizes)), 36 | sizes: sizes, 37 | } 38 | b.iovecs = make([]unix.Iovec, len(b.views)) 39 | return b 40 | } 41 | 42 | func (b *iovecBuffer) nextIovecs() []unix.Iovec { 43 | vnetHdrOff := 0 44 | for i := range b.views { 45 | if b.views[i] != nil { 46 | break 47 | } 48 | v := buffer.NewView(b.sizes[i]) 49 | b.views[i] = v 50 | b.iovecs[i+vnetHdrOff] = unix.Iovec{Base: &v[0]} 51 | b.iovecs[i+vnetHdrOff].SetLen(len(v)) 52 | } 53 | return b.iovecs 54 | } 55 | 56 | func (b *iovecBuffer) pullViews(n int) buffer.VectorisedView { 57 | var views []buffer.View 58 | c := 0 59 | for i, v := range b.views { 60 | c += len(v) 61 | if c >= n { 62 | b.views[i].CapLength(len(v) - (c - n)) 63 | views = append([]buffer.View(nil), b.views[:i+1]...) 64 | break 65 | } 66 | } 67 | // Remove the first len(views) used views from the state. 68 | for i := range views { 69 | b.views[i] = nil 70 | } 71 | return buffer.NewVectorisedView(n, views) 72 | } 73 | 74 | // stopFd is an eventfd used to signal the stop of a dispatcher. 75 | type stopFd struct { 76 | efd int 77 | } 78 | 79 | func newStopFd() (stopFd, error) { 80 | efd, err := unix.Eventfd(0, unix.EFD_NONBLOCK) 81 | if err != nil { 82 | return stopFd{efd: -1}, fmt.Errorf("failed to create eventfd: %w", err) 83 | } 84 | return stopFd{efd: efd}, nil 85 | } 86 | 87 | // stop writes to the eventfd and notifies the dispatcher to stop. It does not 88 | // block. 89 | func (s *stopFd) stop() { 90 | increment := []byte{1, 0, 0, 0, 0, 0, 0, 0} 91 | if n, err := unix.Write(s.efd, increment); n != len(increment) || err != nil { 92 | // There are two possible errors documented in eventfd(2) for writing: 93 | // 1. We are writing 8 bytes and not 0xffffffffffffff, thus no EINVAL. 94 | // 2. stop is only supposed to be called once, it can't reach the limit, 95 | // thus no EAGAIN. 96 | panic(fmt.Sprintf("write(efd) = (%d, %s), want (%d, nil)", n, err, len(increment))) 97 | } 98 | } 99 | 100 | // readVDispatcher uses readv() system call to read inbound packets and 101 | // dispatches them. 102 | type readVDispatcher struct { 103 | stopFd 104 | // fd is the file descriptor used to send and receive packets. 105 | fd int 106 | 107 | // e is the endpoint this dispatcher is attached to. 108 | e *rwEndpoint 109 | 110 | // buf is the iovec buffer that contains the packet contents. 111 | buf *iovecBuffer 112 | } 113 | 114 | func newReadVDispatcher(fd int, e *rwEndpoint) (*readVDispatcher, error) { 115 | stopFd, err := newStopFd() 116 | if err != nil { 117 | return nil, err 118 | } 119 | d := &readVDispatcher{ 120 | stopFd: stopFd, 121 | fd: fd, 122 | e: e, 123 | } 124 | d.buf = newIovecBuffer(bufConfig) 125 | return d, nil 126 | } 127 | 128 | // dispatch reads one packet from the file descriptor and dispatches it. 129 | func (d *readVDispatcher) dispatch() (bool, tcpip.Error) { 130 | n, err := rawfile.BlockingReadvUntilStopped(d.efd, d.fd, d.buf.nextIovecs()) 131 | if n <= 0 || err != nil { 132 | return false, err 133 | } 134 | 135 | pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ 136 | Data: d.buf.pullViews(n), 137 | IsForwardedPacket: true, 138 | }) 139 | defer pkt.DecRef() 140 | 141 | var p tcpip.NetworkProtocolNumber 142 | 143 | // We don't get any indication of what the packet is, so try to guess 144 | // if it's an IPv4 or IPv6 packet. 145 | // IP version information is at the first octet, so pulling up 1 byte. 146 | h, ok := pkt.Data().PullUp(1) 147 | if !ok { 148 | return true, nil 149 | } 150 | switch header.IPVersion(h) { 151 | case header.IPv4Version: 152 | p = header.IPv4ProtocolNumber 153 | case header.IPv6Version: 154 | p = header.IPv6ProtocolNumber 155 | default: 156 | return true, nil 157 | } 158 | 159 | d.e.dispatcher.DeliverNetworkPacket(p, pkt) 160 | 161 | return true, nil 162 | } 163 | -------------------------------------------------------------------------------- /gvisor/endpoint.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "sync" 5 | 6 | "golang.org/x/sys/unix" 7 | "gvisor.dev/gvisor/pkg/tcpip" 8 | "gvisor.dev/gvisor/pkg/tcpip/header" 9 | "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" 10 | "gvisor.dev/gvisor/pkg/tcpip/stack" 11 | ) 12 | 13 | var _ stack.InjectableLinkEndpoint = (*rwEndpoint)(nil) 14 | 15 | // rwEndpoint implements the interface of stack.LinkEndpoint from io.ReadWriter. 16 | type rwEndpoint struct { 17 | fd int 18 | 19 | // mtu (maximum transmission unit) is the maximum size of a packet. 20 | mtu uint32 21 | wg sync.WaitGroup 22 | 23 | inbound *readVDispatcher 24 | dispatcher stack.NetworkDispatcher 25 | } 26 | 27 | func newRwEndpoint(dev int32, mtu int32) (*rwEndpoint, error) { 28 | e := &rwEndpoint{ 29 | fd: int(dev), 30 | mtu: uint32(mtu), 31 | } 32 | i, err := newReadVDispatcher(e.fd, e) 33 | if err != nil { 34 | return nil, err 35 | } 36 | e.inbound = i 37 | return e, nil 38 | } 39 | 40 | func (e *rwEndpoint) InjectInbound(networkProtocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { 41 | go e.dispatcher.DeliverNetworkPacket(networkProtocol, pkt) 42 | } 43 | 44 | func (e *rwEndpoint) InjectOutbound(dest tcpip.Address, packet []byte) tcpip.Error { 45 | return rawfile.NonBlockingWrite(e.fd, packet) 46 | } 47 | 48 | // Attach launches the goroutine that reads packets from io.ReadWriter and 49 | // dispatches them via the provided dispatcher. 50 | func (e *rwEndpoint) Attach(dispatcher stack.NetworkDispatcher) { 51 | if dispatcher == nil && e.dispatcher != nil { 52 | e.inbound.stop() 53 | e.Wait() 54 | e.dispatcher = nil 55 | return 56 | } 57 | if dispatcher != nil && e.dispatcher == nil { 58 | e.dispatcher = dispatcher 59 | e.wg.Add(1) 60 | go func() { 61 | e.dispatchLoop(e.inbound) 62 | e.wg.Done() 63 | }() 64 | } 65 | } 66 | 67 | // IsAttached implements stack.LinkEndpoint.IsAttached. 68 | func (e *rwEndpoint) IsAttached() bool { 69 | return e.dispatcher != nil 70 | } 71 | 72 | // dispatchLoop reads packets from the file descriptor in a loop and dispatches 73 | // them to the network stack. 74 | func (e *rwEndpoint) dispatchLoop(inboundDispatcher *readVDispatcher) tcpip.Error { 75 | for { 76 | cont, err := inboundDispatcher.dispatch() 77 | if err != nil || !cont { 78 | return err 79 | } 80 | } 81 | } 82 | 83 | // WritePackets writes packets back into io.ReadWriter. 84 | func (e *rwEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { 85 | // Preallocate to avoid repeated reallocation as we append to batch. 86 | // batchSz is 47 because when SWGSO is in use then a single 65KB TCP 87 | // segment can get split into 46 segments of 1420 bytes and a single 216 88 | // byte segment. 89 | const batchSz = 47 90 | batch := make([]unix.Iovec, 0, batchSz) 91 | for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { 92 | views := pkt.Views() 93 | for _, v := range views { 94 | batch = rawfile.AppendIovecFromBytes(batch, v, rawfile.MaxIovs) 95 | } 96 | } 97 | err := rawfile.NonBlockingWriteIovec(e.fd, batch) 98 | if err != nil { 99 | return 0, err 100 | } 101 | return pkts.Len(), nil 102 | } 103 | 104 | // MTU implements stack.LinkEndpoint.MTU. 105 | func (e *rwEndpoint) MTU() uint32 { 106 | return e.mtu 107 | } 108 | 109 | // Capabilities implements stack.LinkEndpoint.Capabilities. 110 | func (e *rwEndpoint) Capabilities() stack.LinkEndpointCapabilities { 111 | return stack.CapabilityNone 112 | } 113 | 114 | // MaxHeaderLength returns the maximum size of the link layer header. Given it 115 | // doesn't have a header, it just returns 0. 116 | func (*rwEndpoint) MaxHeaderLength() uint16 { 117 | return 0 118 | } 119 | 120 | // LinkAddress returns the link address of this endpoint. 121 | func (*rwEndpoint) LinkAddress() tcpip.LinkAddress { 122 | return "" 123 | } 124 | 125 | // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. 126 | func (*rwEndpoint) ARPHardwareType() header.ARPHardwareType { 127 | return header.ARPHardwareNone 128 | } 129 | 130 | func (e *rwEndpoint) AddHeader(*stack.PacketBuffer) { 131 | } 132 | 133 | // Wait implements stack.LinkEndpoint.Wait. 134 | func (e *rwEndpoint) Wait() { 135 | e.wg.Wait() 136 | } 137 | -------------------------------------------------------------------------------- /gvisor/errors.generated.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/errors" 7 | ) 8 | 9 | type errPathObjHolder struct{} 10 | 11 | func newError(values ...interface{}) *errors.Error { 12 | return errors.New(values...).WithPathObj(errPathObjHolder{}) 13 | } 14 | 15 | func newErrorf(format string, a ...interface{}) *errors.Error { 16 | return errors.New(fmt.Sprintf(format, a)).WithPathObj(errPathObjHolder{}) 17 | } 18 | -------------------------------------------------------------------------------- /gvisor/gvisor.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "os" 7 | 8 | "github.com/sirupsen/logrus" 9 | "github.com/v2fly/v2ray-core/v5/common/buf" 10 | "gvisor.dev/gvisor/pkg/tcpip" 11 | "gvisor.dev/gvisor/pkg/tcpip/header" 12 | "gvisor.dev/gvisor/pkg/tcpip/link/sniffer" 13 | "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" 14 | "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" 15 | "gvisor.dev/gvisor/pkg/tcpip/stack" 16 | "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" 17 | "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" 18 | "gvisor.dev/gvisor/pkg/tcpip/transport/udp" 19 | "libcore/comm" 20 | "libcore/tun" 21 | ) 22 | 23 | //go:generate go run ../errorgen 24 | 25 | var _ tun.Tun = (*GVisor)(nil) 26 | 27 | type GVisor struct { 28 | Endpoint stack.LinkEndpoint 29 | PcapFile *os.File 30 | Stack *stack.Stack 31 | } 32 | 33 | func (t *GVisor) Close() error { 34 | t.Stack.Close() 35 | if t.PcapFile != nil { 36 | _ = t.PcapFile.Close() 37 | } 38 | return nil 39 | } 40 | 41 | const DefaultNIC tcpip.NICID = 0x01 42 | 43 | func New(dev int32, mtu int32, handler tun.Handler, nicId tcpip.NICID, pcap bool, pcapFile *os.File, snapLen uint32, ipv6Mode int32) (*GVisor, error) { 44 | var endpoint stack.LinkEndpoint 45 | endpoint, _ = newRwEndpoint(dev, mtu) 46 | if pcap { 47 | pcapEndpoint, err := sniffer.NewWithWriter(endpoint, &pcapFileWrapper{pcapFile}, snapLen) 48 | if err != nil { 49 | return nil, err 50 | } 51 | endpoint = pcapEndpoint 52 | } 53 | var o stack.Options 54 | switch ipv6Mode { 55 | case comm.IPv6Disable: 56 | o = stack.Options{ 57 | NetworkProtocols: []stack.NetworkProtocolFactory{ 58 | ipv4.NewProtocol, 59 | }, 60 | TransportProtocols: []stack.TransportProtocolFactory{ 61 | tcp.NewProtocol, 62 | udp.NewProtocol, 63 | icmp.NewProtocol4, 64 | }, 65 | } 66 | case comm.IPv6Only: 67 | o = stack.Options{ 68 | NetworkProtocols: []stack.NetworkProtocolFactory{ 69 | ipv6.NewProtocol, 70 | }, 71 | TransportProtocols: []stack.TransportProtocolFactory{ 72 | tcp.NewProtocol, 73 | udp.NewProtocol, 74 | icmp.NewProtocol6, 75 | }, 76 | } 77 | default: 78 | o = stack.Options{ 79 | NetworkProtocols: []stack.NetworkProtocolFactory{ 80 | ipv4.NewProtocol, 81 | ipv6.NewProtocol, 82 | }, 83 | TransportProtocols: []stack.TransportProtocolFactory{ 84 | tcp.NewProtocol, 85 | udp.NewProtocol, 86 | icmp.NewProtocol4, 87 | icmp.NewProtocol6, 88 | }, 89 | } 90 | } 91 | s := stack.New(o) 92 | s.SetRouteTable([]tcpip.Route{ 93 | { 94 | Destination: header.IPv4EmptySubnet, 95 | NIC: nicId, 96 | }, 97 | { 98 | Destination: header.IPv6EmptySubnet, 99 | NIC: nicId, 100 | }, 101 | }) 102 | 103 | bufSize := buf.Size 104 | s.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{ 105 | Min: 1, 106 | Default: bufSize, 107 | Max: bufSize, 108 | }) 109 | s.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{ 110 | Min: 1, 111 | Default: bufSize, 112 | Max: bufSize, 113 | }) 114 | 115 | sOpt := tcpip.TCPSACKEnabled(true) 116 | s.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt) 117 | 118 | mOpt := tcpip.TCPModerateReceiveBufferOption(true) 119 | s.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt) 120 | 121 | gTcpHandler(s, handler) 122 | gUdpHandler(s, handler) 123 | gIcmpHandler(s, endpoint, handler) 124 | gMust(s.CreateNIC(nicId, endpoint)) 125 | gMust(s.SetSpoofing(nicId, true)) 126 | gMust(s.SetPromiscuousMode(nicId, true)) 127 | 128 | return &GVisor{endpoint, pcapFile, s}, nil 129 | } 130 | 131 | type pcapFileWrapper struct { 132 | io.Writer 133 | } 134 | 135 | func (w *pcapFileWrapper) Write(p []byte) (n int, err error) { 136 | n, err = w.Writer.Write(p) 137 | if err != nil { 138 | logrus.Debug("write pcap file failed: ", err) 139 | } 140 | return n, nil 141 | } 142 | 143 | func gMust(err tcpip.Error) { 144 | if err != nil { 145 | logrus.Panicln(err.String()) 146 | } 147 | } 148 | 149 | func tcpipErr(err tcpip.Error) error { 150 | return errors.New(err.String()) 151 | } 152 | -------------------------------------------------------------------------------- /gvisor/icmp.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "github.com/v2fly/v2ray-core/v5/common/buf" 5 | "github.com/v2fly/v2ray-core/v5/common/net" 6 | "golang.org/x/sys/unix" 7 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 8 | "gvisor.dev/gvisor/pkg/tcpip/header" 9 | "gvisor.dev/gvisor/pkg/tcpip/stack" 10 | "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" 11 | "libcore/tun" 12 | ) 13 | 14 | func gIcmpHandler(s *stack.Stack, ep stack.LinkEndpoint, handler tun.Handler) { 15 | s.SetTransportProtocolHandler(icmp.ProtocolNumber4, func(id stack.TransportEndpointID, packet *stack.PacketBuffer) bool { 16 | hdr := header.ICMPv4(packet.TransportHeader().View()) 17 | if hdr.Type() != header.ICMPv4Echo { 18 | return false 19 | } 20 | 21 | source := net.Destination{Address: net.IPAddress([]byte(id.RemoteAddress)), Network: net.Network_UDP} 22 | destination := net.Destination{Address: net.IPAddress([]byte(id.LocalAddress)), Port: 7, Network: net.Network_UDP} 23 | 24 | originVV := buffer.VectorisedView{} 25 | originVV.AppendView(packet.NetworkHeader().View()) 26 | transportData := packet.TransportHeader().View() 27 | if transportData.Size() > 8 { 28 | transportData = transportData[:8] 29 | } 30 | originVV.AppendView(transportData) 31 | originHdr := originVV.ToOwnedView() 32 | 33 | ipHdr := header.IPv4(packet.NetworkHeader().View()) 34 | sourceAddress := ipHdr.SourceAddress() 35 | ipHdr.SetSourceAddress(ipHdr.DestinationAddress()) 36 | ipHdr.SetDestinationAddress(sourceAddress) 37 | ipHdr.SetChecksum(0) 38 | ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) 39 | 40 | dataVV := buffer.VectorisedView{} 41 | dataVV.AppendView(packet.TransportHeader().View()) 42 | dataVV.Append(packet.Data().ExtractVV()) 43 | data := dataVV.ToView() 44 | messageLen := len(data) 45 | 46 | netHdr := packet.NetworkHeader().View() 47 | if !handler.NewPingPacket(source, destination, buf.FromBytes(data), func(message []byte) error { 48 | icmpHdr := header.ICMPv4(message) 49 | if icmpHdr.Type() == header.ICMPv4DstUnreachable { 50 | const ICMPv4HeaderSize = 4 51 | unreachableHdr := header.ICMPv4(buffer.NewView(header.ICMPv4MinimumErrorPayloadSize + len(originHdr))) 52 | copy(unreachableHdr[:ICMPv4HeaderSize], message) 53 | copy(unreachableHdr[header.ICMPv4MinimumErrorPayloadSize:], originHdr) 54 | icmpHdr = unreachableHdr 55 | } 56 | 57 | backData := buffer.VectorisedView{} 58 | 59 | if len(icmpHdr) != messageLen { 60 | backIpHdr := header.IPv4(buffer.NewViewFromBytes(netHdr)) 61 | oldLen := backIpHdr.TotalLength() 62 | backIpHdr.SetTotalLength(uint16(len(netHdr) + len(message))) 63 | backIpHdr.SetChecksum(^header.ChecksumCombine(^backIpHdr.Checksum(), header.ChecksumCombine(backIpHdr.TotalLength(), ^oldLen))) 64 | backData.AppendView(buffer.View(backIpHdr)) 65 | } else { 66 | backData.AppendView(netHdr) 67 | } 68 | 69 | backData.AppendView(buffer.View(icmpHdr)) 70 | backPacket := stack.NewPacketBuffer(stack.PacketBufferOptions{Data: backData}) 71 | defer backPacket.DecRef() 72 | var packetList stack.PacketBufferList 73 | packetList.PushFront(backPacket) 74 | _, err := ep.WritePackets(packetList) 75 | if err != nil { 76 | return newError("failed to write packet to device: ", err.String()) 77 | } 78 | 79 | if icmpHdr.Type() == header.ICMPv4DstUnreachable { 80 | return unix.ENETUNREACH 81 | } 82 | 83 | return nil 84 | }, nil) { 85 | hdr.SetType(header.ICMPv4EchoReply) 86 | hdr.SetChecksum(0) 87 | hdr.SetChecksum(header.ICMPv4Checksum(hdr, packet.Data().AsRange().Checksum())) 88 | var packetList stack.PacketBufferList 89 | packetList.PushFront(packet) 90 | _, err := ep.WritePackets(packetList) 91 | if err != nil { 92 | newError("failed to write packet to device: ", err.String()).AtWarning().WriteToLog() 93 | return false 94 | } 95 | } 96 | 97 | return true 98 | }) 99 | s.SetTransportProtocolHandler(icmp.ProtocolNumber6, func(id stack.TransportEndpointID, packet *stack.PacketBuffer) bool { 100 | hdr := header.ICMPv6(packet.TransportHeader().View()) 101 | if hdr.Type() != header.ICMPv6EchoRequest { 102 | return false 103 | } 104 | 105 | source := net.Destination{Address: net.IPAddress([]byte(id.RemoteAddress)), Network: net.Network_UDP} 106 | destination := net.Destination{Address: net.IPAddress([]byte(id.LocalAddress)), Port: 7, Network: net.Network_UDP} 107 | 108 | originVV := buffer.VectorisedView{} 109 | originVV.AppendView(packet.NetworkHeader().View()) 110 | transportData := packet.TransportHeader().View() 111 | if transportData.Size() > 8 { 112 | transportData = transportData[:8] 113 | } 114 | originVV.AppendView(transportData) 115 | originHdr := originVV.ToOwnedView() 116 | 117 | ipHdr := header.IPv6(packet.NetworkHeader().View()) 118 | sourceAddress := ipHdr.SourceAddress() 119 | ipHdr.SetSourceAddress(ipHdr.DestinationAddress()) 120 | ipHdr.SetDestinationAddress(sourceAddress) 121 | 122 | dataVV := buffer.VectorisedView{} 123 | dataVV.AppendView(packet.TransportHeader().View()) 124 | dataVV.Append(packet.Data().ExtractVV()) 125 | data := dataVV.ToView() 126 | messageLen := len(data) 127 | 128 | netHdr := packet.NetworkHeader().View() 129 | if !handler.NewPingPacket(source, destination, buf.FromBytes(data), func(message []byte) error { 130 | icmpHdr := header.ICMPv6(message) 131 | if icmpHdr.Type() == header.ICMPv6DstUnreachable { 132 | unreachableHdr := header.ICMPv6(buffer.NewView(header.ICMPv6DstUnreachableMinimumSize + len(originHdr))) 133 | copy(unreachableHdr[:header.ICMPv6HeaderSize], message) 134 | copy(unreachableHdr[header.ICMPv6DstUnreachableMinimumSize:], originHdr) 135 | icmpHdr = unreachableHdr 136 | } 137 | 138 | backData := buffer.VectorisedView{} 139 | 140 | if len(icmpHdr) != messageLen { 141 | backIpHdr := header.IPv6(buffer.NewViewFromBytes(netHdr)) 142 | backIpHdr.SetPayloadLength(uint16(len(icmpHdr))) 143 | backData.AppendView(buffer.View(backIpHdr)) 144 | } else { 145 | backData.AppendView(netHdr) 146 | } 147 | 148 | backData.AppendView(buffer.View(icmpHdr)) 149 | 150 | icmpHdr.SetChecksum(0) 151 | icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 152 | Header: icmpHdr, 153 | Src: id.RemoteAddress, 154 | Dst: id.LocalAddress, 155 | })) 156 | 157 | backPacket := stack.NewPacketBuffer(stack.PacketBufferOptions{Data: backData}) 158 | defer backPacket.DecRef() 159 | var packetList stack.PacketBufferList 160 | packetList.PushFront(backPacket) 161 | _, err := ep.WritePackets(packetList) 162 | if err != nil { 163 | return newError("failed to write packet to device: ", err.String()) 164 | } 165 | 166 | if icmpHdr.Type() == header.ICMPv6DstUnreachable { 167 | return unix.ENETUNREACH 168 | } 169 | 170 | return nil 171 | }, nil) { 172 | hdr.SetType(header.ICMPv6EchoReply) 173 | hdr.SetChecksum(0) 174 | hdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 175 | Header: hdr, 176 | Src: id.LocalAddress, 177 | Dst: id.RemoteAddress, 178 | PayloadCsum: packet.Data().AsRange().Checksum(), 179 | PayloadLen: packet.Data().Size(), 180 | })) 181 | var packetList stack.PacketBufferList 182 | packetList.PushFront(packet) 183 | _, err := ep.WritePackets(packetList) 184 | if err != nil { 185 | newError("failed to write packet to device: ", err.String()).AtWarning().WriteToLog() 186 | return false 187 | } 188 | } 189 | return true 190 | }) 191 | } 192 | -------------------------------------------------------------------------------- /gvisor/tcp.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | "time" 8 | 9 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 10 | "gvisor.dev/gvisor/pkg/tcpip" 11 | "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" 12 | "gvisor.dev/gvisor/pkg/tcpip/stack" 13 | "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" 14 | "gvisor.dev/gvisor/pkg/waiter" 15 | "libcore/tun" 16 | ) 17 | 18 | func gTcpHandler(s *stack.Stack, handler tun.Handler) { 19 | forwarder := tcp.NewForwarder(s, 0, 1024, func(request *tcp.ForwarderRequest) { 20 | id := request.ID() 21 | waitQueue := new(waiter.Queue) 22 | endpoint, errT := request.CreateEndpoint(waitQueue) 23 | if errT != nil { 24 | newError("failed to create TCP connection").Base(tcpipErr(errT)).WriteToLog() 25 | // prevent potential half-open TCP connection leak. 26 | request.Complete(true) 27 | return 28 | } 29 | request.Complete(false) 30 | srcAddr := net.JoinHostPort(id.RemoteAddress.String(), strconv.Itoa(int(id.RemotePort))) 31 | src, err := v2rayNet.ParseDestination(fmt.Sprint("tcp:", srcAddr)) 32 | if err != nil { 33 | newError("[TCP] parse source address ", srcAddr, " failed: ", err).AtWarning().WriteToLog() 34 | return 35 | } 36 | dstAddr := net.JoinHostPort(id.LocalAddress.String(), strconv.Itoa(int(id.LocalPort))) 37 | dst, err := v2rayNet.ParseDestination(fmt.Sprint("tcp:", dstAddr)) 38 | if err != nil { 39 | newError("[TCP] parse destination address ", dstAddr, " failed: ", err).AtWarning().WriteToLog() 40 | return 41 | } 42 | go handler.NewConnection(src, dst, gTcpConn{endpoint, gonet.NewTCPConn(waitQueue, endpoint)}) 43 | }) 44 | s.SetTransportProtocolHandler(tcp.ProtocolNumber, forwarder.HandlePacket) 45 | } 46 | 47 | type gTcpConn struct { 48 | ep tcpip.Endpoint 49 | *gonet.TCPConn 50 | } 51 | 52 | func (g gTcpConn) Close() error { 53 | g.ep.Close() 54 | g.TCPConn.SetDeadline(time.Now().Add(-1)) 55 | return g.TCPConn.Close() 56 | } 57 | -------------------------------------------------------------------------------- /gvisor/udp.go: -------------------------------------------------------------------------------- 1 | package gvisor 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/v2fly/v2ray-core/v5/common/buf" 9 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 10 | "gvisor.dev/gvisor/pkg/tcpip" 11 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 12 | "gvisor.dev/gvisor/pkg/tcpip/header" 13 | "gvisor.dev/gvisor/pkg/tcpip/stack" 14 | "gvisor.dev/gvisor/pkg/tcpip/transport/udp" 15 | "libcore/tun" 16 | ) 17 | 18 | func gUdpHandler(s *stack.Stack, handler tun.Handler) { 19 | s.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, buffer *stack.PacketBuffer) bool { 20 | // Ref: gVisor pkg/tcpip/transport/udp/endpoint.go HandlePacket 21 | udpHdr := header.UDP(buffer.TransportHeader().View()) 22 | if int(udpHdr.Length()) > buffer.Data().Size()+header.UDPMinimumSize { 23 | // Malformed packet. 24 | return true 25 | } 26 | 27 | srcAddr := net.JoinHostPort(id.RemoteAddress.String(), strconv.Itoa(int(id.RemotePort))) 28 | src, err := v2rayNet.ParseDestination(fmt.Sprint("udp:", srcAddr)) 29 | if err != nil { 30 | newError("[UDP] parse source address ", srcAddr, " failed: ", err).AtWarning().WriteToLog() 31 | return true 32 | } 33 | dstAddr := net.JoinHostPort(id.LocalAddress.String(), strconv.Itoa(int(id.LocalPort))) 34 | dst, err := v2rayNet.ParseDestination(fmt.Sprint("udp:", dstAddr)) 35 | if err != nil { 36 | newError("[UDP] parse destination address ", dstAddr, " failed: ", err).AtWarning().WriteToLog() 37 | return true 38 | } 39 | 40 | data := buffer.Data().ExtractVV() 41 | packet := &gUdpPacket{ 42 | s: s, 43 | id: &id, 44 | nicID: buffer.NICID, 45 | netHdr: buffer.Network(), 46 | netProto: buffer.NetworkProtocolNumber, 47 | } 48 | destUdpAddr := &net.UDPAddr{ 49 | IP: dst.Address.IP(), 50 | Port: int(dst.Port), 51 | } 52 | go handler.NewPacket(src, dst, buf.FromBytes(data.ToView()), func(bytes []byte, addr *net.UDPAddr) (int, error) { 53 | if addr == nil { 54 | addr = destUdpAddr 55 | } 56 | return packet.WriteBack(bytes, addr) 57 | }, nil) 58 | return true 59 | }) 60 | } 61 | 62 | type gUdpPacket struct { 63 | s *stack.Stack 64 | id *stack.TransportEndpointID 65 | nicID tcpip.NICID 66 | netHdr header.Network 67 | netProto tcpip.NetworkProtocolNumber 68 | } 69 | 70 | func (p *gUdpPacket) WriteBack(b []byte, addr *net.UDPAddr) (int, error) { 71 | v := buffer.View(b) 72 | if len(v) > header.UDPMaximumPacketSize { 73 | // Payload can't possibly fit in a packet. 74 | return 0, fmt.Errorf("%s", &tcpip.ErrMessageTooLong{}) 75 | } 76 | 77 | var ( 78 | localAddress tcpip.Address 79 | localPort uint16 80 | ) 81 | 82 | if addr == nil { 83 | localAddress = p.netHdr.DestinationAddress() 84 | localPort = p.id.LocalPort 85 | } else { 86 | localAddress = tcpip.Address(addr.IP) 87 | localPort = uint16(addr.Port) 88 | } 89 | 90 | route, err := p.s.FindRoute(p.nicID, localAddress, p.netHdr.SourceAddress(), p.netProto, false /* multicastLoop */) 91 | if err != nil { 92 | return 0, fmt.Errorf("%#v find route: %s", p.id, err) 93 | } 94 | defer route.Release() 95 | 96 | data := v.ToVectorisedView() 97 | if err = gSendUDP(route, data, localPort, p.id.RemotePort); err != nil { 98 | return 0, fmt.Errorf("%v", err) 99 | } 100 | return data.Size(), nil 101 | } 102 | 103 | // gSendUDP sends a UDP segment via the provided network endpoint and under the 104 | // provided identity. 105 | func gSendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16) tcpip.Error { 106 | pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ 107 | ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), 108 | Data: data, 109 | }) 110 | defer pkt.DecRef() 111 | 112 | // Initialize the UDP header. 113 | udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize)) 114 | pkt.TransportProtocolNumber = udp.ProtocolNumber 115 | 116 | length := uint16(pkt.Size()) 117 | udpHdr.Encode(&header.UDPFields{ 118 | SrcPort: localPort, 119 | DstPort: remotePort, 120 | Length: length, 121 | }) 122 | 123 | // Set the checksum field unless TX checksum offload is enabled. 124 | // On IPv4, UDP checksum is optional, and a zero value indicates the 125 | // transmitter skipped the checksum generation (RFC768). 126 | // On IPv6, UDP checksum is not optional (RFC2460 Section 8.1). 127 | if r.RequiresTXTransportChecksum() && r.NetProto() == header.IPv6ProtocolNumber { 128 | xsum := r.PseudoHeaderChecksum(udp.ProtocolNumber, length) 129 | for _, v := range data.Views() { 130 | xsum = header.Checksum(v, xsum) 131 | } 132 | udpHdr.SetChecksum(^udpHdr.CalculateChecksum(xsum)) 133 | } 134 | 135 | ttl := r.DefaultTTL() 136 | 137 | if err := r.WritePacket(stack.NetworkHeaderParams{ 138 | Protocol: udp.ProtocolNumber, 139 | TTL: ttl, 140 | TOS: 0, /* default */ 141 | }, pkt); err != nil { 142 | r.Stats().UDP.PacketSendErrors.Increment() 143 | return err 144 | } 145 | 146 | // Track count of packets sent. 147 | r.Stats().UDP.PacketsSent.Increment() 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/sha256" 7 | "crypto/tls" 8 | "crypto/x509" 9 | "encoding/hex" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "math/rand" 15 | "net" 16 | "net/http" 17 | "net/url" 18 | "os" 19 | "strconv" 20 | "sync" 21 | 22 | "github.com/Dreamacro/clash/transport/socks5" 23 | "github.com/v2fly/v2ray-core/v5/common" 24 | "github.com/v2fly/v2ray-core/v5/common/buf" 25 | ) 26 | 27 | type HTTPClient interface { 28 | RestrictedTLS() 29 | ModernTLS() 30 | PinnedTLS12() 31 | PinnedSHA256(sumHex string) 32 | TrySocks5(port int32) 33 | KeepAlive() 34 | NewRequest() HTTPRequest 35 | Close() 36 | } 37 | 38 | type HTTPRequest interface { 39 | SetURL(link string) error 40 | SetMethod(method string) 41 | SetHeader(key string, value string) 42 | SetContent(content []byte) 43 | SetContentString(content string) 44 | RandomUserAgent() 45 | SetUserAgent(userAgent string) 46 | Execute() (HTTPResponse, error) 47 | } 48 | 49 | type HTTPResponse interface { 50 | GetContent() ([]byte, error) 51 | GetContentString() (string, error) 52 | WriteTo(path string) error 53 | } 54 | 55 | var ( 56 | _ HTTPClient = (*httpClient)(nil) 57 | _ HTTPRequest = (*httpRequest)(nil) 58 | _ HTTPResponse = (*httpResponse)(nil) 59 | ) 60 | 61 | type httpClient struct { 62 | tls tls.Config 63 | client http.Client 64 | transport http.Transport 65 | } 66 | 67 | func NewHttpClient() HTTPClient { 68 | client := new(httpClient) 69 | client.client.Transport = &client.transport 70 | client.transport.TLSClientConfig = &client.tls 71 | client.transport.DisableKeepAlives = true 72 | return client 73 | } 74 | 75 | func (c *httpClient) ModernTLS() { 76 | c.tls.MinVersion = tls.VersionTLS12 77 | c.tls.CipherSuites = common.Map(tls.CipherSuites(), func(it *tls.CipherSuite) uint16 { return it.ID }) 78 | } 79 | 80 | func (c *httpClient) RestrictedTLS() { 81 | c.tls.MinVersion = tls.VersionTLS13 82 | c.tls.CipherSuites = common.Map(common.Filter(tls.CipherSuites(), func(it *tls.CipherSuite) bool { 83 | return common.Contains(it.SupportedVersions, uint16(tls.VersionTLS13)) 84 | }), func(it *tls.CipherSuite) uint16 { 85 | return it.ID 86 | }) 87 | } 88 | 89 | func (c *httpClient) PinnedTLS12() { 90 | c.tls.MinVersion = tls.VersionTLS12 91 | c.tls.MaxVersion = tls.VersionTLS12 92 | } 93 | 94 | func (c *httpClient) PinnedSHA256(sumHex string) { 95 | c.tls.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 96 | for _, rawCert := range rawCerts { 97 | certSum := sha256.Sum256(rawCert) 98 | if sumHex == hex.EncodeToString(certSum[:]) { 99 | return nil 100 | } 101 | } 102 | return newError("pinned sha256 sum mismatch") 103 | } 104 | } 105 | 106 | func (c *httpClient) TrySocks5(port int32) { 107 | dialer := new(net.Dialer) 108 | c.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { 109 | for { 110 | socksConn, err := dialer.DialContext(ctx, "tcp", "127.0.0.1:"+strconv.Itoa(int(port))) 111 | if err != nil { 112 | break 113 | } 114 | _, err = socks5.ClientHandshake(socksConn, socks5.ParseAddr(addr), socks5.CmdConnect, nil) 115 | if err != nil { 116 | break 117 | } 118 | return socksConn, err 119 | } 120 | return dialer.DialContext(ctx, network, addr) 121 | } 122 | } 123 | 124 | func (c *httpClient) KeepAlive() { 125 | c.transport.ForceAttemptHTTP2 = true 126 | c.transport.DisableKeepAlives = false 127 | } 128 | 129 | func (c *httpClient) NewRequest() HTTPRequest { 130 | req := &httpRequest{httpClient: c} 131 | req.request = http.Request{ 132 | Method: "GET", 133 | Header: http.Header{}, 134 | } 135 | return req 136 | } 137 | 138 | func (c *httpClient) Close() { 139 | c.transport.CloseIdleConnections() 140 | } 141 | 142 | type httpRequest struct { 143 | *httpClient 144 | request http.Request 145 | } 146 | 147 | func (r *httpRequest) SetURL(link string) (err error) { 148 | r.request.URL, err = url.Parse(link) 149 | if r.request.URL.User != nil { 150 | user := r.request.URL.User.Username() 151 | password, _ := r.request.URL.User.Password() 152 | r.request.SetBasicAuth(user, password) 153 | } 154 | return 155 | } 156 | 157 | func (r *httpRequest) SetMethod(method string) { 158 | r.request.Method = method 159 | } 160 | 161 | func (r *httpRequest) SetHeader(key string, value string) { 162 | r.request.Header.Set(key, value) 163 | } 164 | 165 | func (r *httpRequest) RandomUserAgent() { 166 | r.request.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2)) 167 | } 168 | 169 | func (r *httpRequest) SetUserAgent(userAgent string) { 170 | r.request.Header.Set("User-Agent", userAgent) 171 | } 172 | 173 | func (r *httpRequest) SetContent(content []byte) { 174 | buffer := bytes.Buffer{} 175 | buffer.Write(content) 176 | r.request.Body = io.NopCloser(bytes.NewReader(buffer.Bytes())) 177 | r.request.ContentLength = int64(len(content)) 178 | } 179 | 180 | func (r *httpRequest) SetContentString(content string) { 181 | r.SetContent([]byte(content)) 182 | } 183 | 184 | func (r *httpRequest) Execute() (HTTPResponse, error) { 185 | response, err := r.client.Do(&r.request) 186 | if err != nil { 187 | return nil, err 188 | } 189 | httpResp := &httpResponse{Response: response} 190 | if response.StatusCode != http.StatusOK { 191 | return nil, errors.New(httpResp.errorString()) 192 | } 193 | return httpResp, nil 194 | } 195 | 196 | type httpResponse struct { 197 | *http.Response 198 | 199 | getContentOnce sync.Once 200 | content []byte 201 | contentError error 202 | } 203 | 204 | func (h *httpResponse) errorString() string { 205 | content, err := h.GetContentString() 206 | if err != nil { 207 | return fmt.Sprint("HTTP ", h.Status) 208 | } 209 | return fmt.Sprint("HTTP ", h.Status, ": ", content) 210 | } 211 | 212 | func (h *httpResponse) GetContent() ([]byte, error) { 213 | h.getContentOnce.Do(func() { 214 | defer h.Body.Close() 215 | h.content, h.contentError = ioutil.ReadAll(h.Body) 216 | }) 217 | return h.content, h.contentError 218 | } 219 | 220 | func (h *httpResponse) GetContentString() (string, error) { 221 | content, err := h.GetContent() 222 | if err != nil { 223 | return "", err 224 | } 225 | return string(content), nil 226 | } 227 | 228 | func (h *httpResponse) WriteTo(path string) error { 229 | defer h.Body.Close() 230 | file, err := os.Create(path) 231 | if err != nil { 232 | return err 233 | } 234 | defer file.Close() 235 | buffer := buf.StackNew() 236 | defer buffer.Release() 237 | _, err = io.CopyBuffer(file, h.Body, buffer.Extend(buf.Size)) 238 | return err 239 | } 240 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .github/env.sh 4 | 5 | go get -v -d 6 | go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20221130124640-349ebaa752ca 7 | gomobile init 8 | go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20221130124640-349ebaa752ca 9 | -------------------------------------------------------------------------------- /io.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "os" 7 | 8 | "github.com/ulikunitz/xz" 9 | "github.com/v2fly/v2ray-core/v5/common/buf" 10 | "libcore/comm" 11 | ) 12 | 13 | type packetConn interface { 14 | readFrom() (buffer *buf.Buffer, addr net.Addr, err error) 15 | writeTo(buffer *buf.Buffer, addr net.Addr) (err error) 16 | io.Closer 17 | } 18 | 19 | func Unxz(archive string, path string) error { 20 | i, err := os.Open(archive) 21 | if err != nil { 22 | return err 23 | } 24 | r, err := xz.NewReader(i) 25 | if err != nil { 26 | comm.CloseIgnore(i) 27 | return err 28 | } 29 | o, err := os.Create(path) 30 | if err != nil { 31 | comm.CloseIgnore(i) 32 | return err 33 | } 34 | _, err = io.Copy(o, r) 35 | comm.CloseIgnore(i, o) 36 | return err 37 | } 38 | 39 | func unxz(path string) error { 40 | err := Unxz(path, path+".tmp") 41 | if err != nil { 42 | return err 43 | } 44 | return os.Rename(path+".tmp", path) 45 | } 46 | -------------------------------------------------------------------------------- /linkname.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SagerNet/LibSagerNetCore/1fce969ea5d102145fc36f480fffdec87598ae54/linkname.s -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | //go:build android 2 | 3 | package libcore 4 | 5 | /* 6 | #cgo LDFLAGS: -landroid -llog 7 | 8 | #include 9 | #include 10 | #include 11 | */ 12 | import "C" 13 | 14 | import ( 15 | "log" 16 | "strings" 17 | "unsafe" 18 | 19 | "github.com/sirupsen/logrus" 20 | appLog "github.com/v2fly/v2ray-core/v5/app/log" 21 | commonLog "github.com/v2fly/v2ray-core/v5/common/log" 22 | ) 23 | 24 | var ( 25 | tag = C.CString("libcore") 26 | tagV2Ray = C.CString("v2ray-core") 27 | ) 28 | 29 | var levels = []logrus.Level{ 30 | logrus.PanicLevel, 31 | logrus.FatalLevel, 32 | logrus.ErrorLevel, 33 | logrus.WarnLevel, 34 | logrus.InfoLevel, 35 | logrus.DebugLevel, 36 | } 37 | 38 | type androidHook struct{} 39 | 40 | type androidFormatter struct{} 41 | 42 | func (f *androidFormatter) Format(entry *logrus.Entry) ([]byte, error) { 43 | return []byte(entry.Message), nil 44 | } 45 | 46 | func (hook *androidHook) Levels() []logrus.Level { 47 | return levels 48 | } 49 | 50 | func (hook *androidHook) Fire(e *logrus.Entry) error { 51 | formatted, err := logrus.StandardLogger().Formatter.Format(e) 52 | if err != nil { 53 | return err 54 | } 55 | str := C.CString(string(formatted)) 56 | 57 | var priority C.int 58 | switch e.Level { 59 | case logrus.PanicLevel: 60 | priority = C.ANDROID_LOG_FATAL 61 | case logrus.FatalLevel: 62 | priority = C.ANDROID_LOG_FATAL 63 | case logrus.ErrorLevel: 64 | priority = C.ANDROID_LOG_ERROR 65 | case logrus.WarnLevel: 66 | priority = C.ANDROID_LOG_WARN 67 | case logrus.InfoLevel: 68 | priority = C.ANDROID_LOG_INFO 69 | case logrus.DebugLevel: 70 | priority = C.ANDROID_LOG_DEBUG 71 | } 72 | C.__android_log_write(priority, tag, str) 73 | C.free(unsafe.Pointer(str)) 74 | return nil 75 | } 76 | 77 | type v2rayLogWriter struct{} 78 | 79 | func (w *v2rayLogWriter) Write(s string) error { 80 | var priority C.int 81 | if strings.Contains(s, "[Debug]") { 82 | s = strings.Replace(s, "[Debug]", "", 1) 83 | priority = C.ANDROID_LOG_DEBUG 84 | } else if strings.Contains(s, "[Info]") { 85 | s = strings.Replace(s, "[Info]", "", 1) 86 | priority = C.ANDROID_LOG_INFO 87 | } else if strings.Contains(s, "[Warning]") { 88 | s = strings.Replace(s, "[Warning]", "", 1) 89 | priority = C.ANDROID_LOG_WARN 90 | } else if strings.Contains(s, "[Error]") { 91 | s = strings.Replace(s, "[Error]", "", 1) 92 | priority = C.ANDROID_LOG_ERROR 93 | } else { 94 | priority = C.ANDROID_LOG_DEBUG 95 | } 96 | 97 | str := C.CString(strings.TrimSpace(s)) 98 | C.__android_log_write(priority, tagV2Ray, str) 99 | C.free(unsafe.Pointer(str)) 100 | return nil 101 | } 102 | 103 | func (w *v2rayLogWriter) Close() error { 104 | return nil 105 | } 106 | 107 | type stdLogWriter struct{} 108 | 109 | func (stdLogWriter) Write(p []byte) (n int, err error) { 110 | str := C.CString(string(p)) 111 | C.__android_log_write(C.ANDROID_LOG_INFO, tag, str) 112 | C.free(unsafe.Pointer(str)) 113 | return len(p), nil 114 | } 115 | 116 | func init() { 117 | log.SetOutput(stdLogWriter{}) 118 | log.SetFlags(log.Flags() &^ log.LstdFlags) 119 | logrus.SetFormatter(&androidFormatter{}) 120 | logrus.AddHook(&androidHook{}) 121 | 122 | _ = appLog.RegisterHandlerCreator(appLog.LogType_Console, func(lt appLog.LogType, 123 | options appLog.HandlerCreatorOptions, 124 | ) (commonLog.Handler, error) { 125 | return commonLog.NewLogger(func() commonLog.Writer { 126 | return &v2rayLogWriter{} 127 | }), nil 128 | }) 129 | } 130 | -------------------------------------------------------------------------------- /nat/errors.generated.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/errors" 7 | ) 8 | 9 | type errPathObjHolder struct{} 10 | 11 | func newError(values ...interface{}) *errors.Error { 12 | return errors.New(values...).WithPathObj(errPathObjHolder{}) 13 | } 14 | 15 | func newErrorf(format string, a ...interface{}) *errors.Error { 16 | return errors.New(fmt.Sprintf(format, a)).WithPathObj(errPathObjHolder{}) 17 | } 18 | -------------------------------------------------------------------------------- /nat/icmp.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "github.com/v2fly/v2ray-core/v5/common/buf" 5 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 6 | "golang.org/x/sys/unix" 7 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 8 | "gvisor.dev/gvisor/pkg/tcpip/header" 9 | "libcore/comm" 10 | ) 11 | 12 | func (t *SystemTun) processICMPv4(cache *buf.Buffer, ipHdr header.IPv4, hdr header.ICMPv4) bool { 13 | if hdr.Type() != header.ICMPv4Echo || hdr.Code() != header.ICMPv4UnusedCode { 14 | return false 15 | } 16 | 17 | source := v2rayNet.Destination{Address: v2rayNet.IPAddress([]byte(ipHdr.SourceAddress())), Network: v2rayNet.Network_UDP} 18 | destination := v2rayNet.Destination{Address: v2rayNet.IPAddress([]byte(ipHdr.DestinationAddress())), Port: 7, Network: v2rayNet.Network_UDP} 19 | 20 | sourceAddress := ipHdr.SourceAddress() 21 | ipHdr.SetSourceAddress(ipHdr.DestinationAddress()) 22 | ipHdr.SetDestinationAddress(sourceAddress) 23 | ipHdr.SetChecksum(0) 24 | ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) 25 | 26 | headerCache := buf.New() 27 | netHdr := headerCache.ExtendCopy(ipHdr[:ipHdr.HeaderLength()]) 28 | transportDataLen := len(hdr) 29 | if transportDataLen > 8 { 30 | transportDataLen = 8 31 | } 32 | originHdr := headerCache.ExtendCopy(ipHdr[:int(ipHdr.HeaderLength())+transportDataLen]) 33 | messageLen := len(hdr) 34 | 35 | cache.Resize(int32(ipHdr.HeaderLength()), cache.Len()) 36 | if t.handler.NewPingPacket(source, destination, cache, func(message []byte) error { 37 | index := headerCache.Len() 38 | defer func() { 39 | headerCache.Clear() 40 | headerCache.Resize(0, index) 41 | }() 42 | 43 | icmpHdr := header.ICMPv4(message) 44 | if icmpHdr.Type() == header.ICMPv4DstUnreachable { 45 | const ICMPv4HeaderSize = 4 46 | unreachableHdr := header.ICMPv4(headerCache.Extend(int32(header.ICMPv4MinimumErrorPayloadSize + len(originHdr)))) 47 | copy(unreachableHdr[:ICMPv4HeaderSize], message) 48 | copy(unreachableHdr[header.ICMPv4MinimumErrorPayloadSize:], originHdr) 49 | icmpHdr = unreachableHdr 50 | } 51 | 52 | backData := buffer.VectorisedView{} 53 | 54 | if len(icmpHdr) != messageLen { 55 | backIpHdr := header.IPv4(headerCache.ExtendCopy(netHdr)) 56 | oldLen := backIpHdr.TotalLength() 57 | backIpHdr.SetTotalLength(uint16(len(netHdr) + len(message))) 58 | backIpHdr.SetChecksum(^header.ChecksumCombine(^backIpHdr.Checksum(), header.ChecksumCombine(backIpHdr.TotalLength(), ^oldLen))) 59 | backData.AppendView(buffer.View(backIpHdr)) 60 | } else { 61 | backData.AppendView(netHdr) 62 | } 63 | 64 | backData.AppendView(buffer.View(icmpHdr)) 65 | err := t.writeRawPacket(backData) 66 | if err != nil { 67 | return newError("failed to write packet to device: ", err.String()) 68 | } 69 | if icmpHdr.Type() == header.ICMPv4DstUnreachable { 70 | return unix.ENETUNREACH 71 | } 72 | return nil 73 | }, comm.Closer(headerCache.Release)) { 74 | return true 75 | } 76 | hdr.SetType(header.ICMPv4EchoReply) 77 | hdr.SetChecksum(0) 78 | hdr.SetChecksum(header.ICMPv4Checksum(hdr, 0)) 79 | t.writeBuffer(ipHdr) 80 | headerCache.Release() 81 | return false 82 | } 83 | 84 | func (t *SystemTun) processICMPv6(cache *buf.Buffer, ipHdr header.IPv6, hdr header.ICMPv6) bool { 85 | if hdr.Type() != header.ICMPv6EchoRequest || hdr.Code() != header.ICMPv6UnusedCode { 86 | return false 87 | } 88 | 89 | source := v2rayNet.Destination{Address: v2rayNet.IPAddress([]byte(ipHdr.SourceAddress())), Network: v2rayNet.Network_UDP} 90 | destination := v2rayNet.Destination{Address: v2rayNet.IPAddress([]byte(ipHdr.DestinationAddress())), Port: 7, Network: v2rayNet.Network_UDP} 91 | 92 | sourceAddress := ipHdr.SourceAddress() 93 | ipHdr.SetSourceAddress(ipHdr.DestinationAddress()) 94 | ipHdr.SetDestinationAddress(sourceAddress) 95 | 96 | headerLength := len(ipHdr) - int(ipHdr.PayloadLength()) 97 | headerCache := buf.New() 98 | netHdr := headerCache.ExtendCopy(ipHdr[:headerLength]) 99 | transportDataLen := len(hdr) 100 | if transportDataLen > 8 { 101 | transportDataLen = 8 102 | } 103 | originHdr := headerCache.ExtendCopy(ipHdr[:headerLength+transportDataLen]) 104 | messageLen := len(hdr) 105 | 106 | cache.Resize(int32(headerLength), cache.Len()) 107 | if t.handler.NewPingPacket(source, destination, cache, func(message []byte) error { 108 | index := headerCache.Len() 109 | defer func() { 110 | headerCache.Clear() 111 | headerCache.Resize(0, index) 112 | }() 113 | 114 | icmpHdr := header.ICMPv6(message) 115 | if icmpHdr.Type() == header.ICMPv6DstUnreachable { 116 | unreachableHdr := header.ICMPv6(headerCache.Extend(int32(header.ICMPv6DstUnreachableMinimumSize + len(originHdr)))) 117 | copy(unreachableHdr[:header.ICMPv6HeaderSize], message) 118 | copy(unreachableHdr[header.ICMPv6DstUnreachableMinimumSize:], originHdr) 119 | icmpHdr = unreachableHdr 120 | } 121 | 122 | backData := buffer.VectorisedView{} 123 | 124 | if len(icmpHdr) != messageLen { 125 | backIpHdr := header.IPv6(headerCache.ExtendCopy(netHdr)) 126 | backIpHdr.SetPayloadLength(uint16(len(icmpHdr))) 127 | backData.AppendView(buffer.View(backIpHdr)) 128 | } else { 129 | backData.AppendView(netHdr) 130 | } 131 | 132 | icmpHdr.SetChecksum(0) 133 | icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 134 | Header: icmpHdr, 135 | Src: ipHdr.SourceAddress(), 136 | Dst: ipHdr.DestinationAddress(), 137 | })) 138 | 139 | backData.AppendView(buffer.View(icmpHdr)) 140 | err := t.writeRawPacket(backData) 141 | if err != nil { 142 | return newError("failed to write packet to device: ", err.String()) 143 | } 144 | if icmpHdr.Type() == header.ICMPv6DstUnreachable { 145 | return unix.ENETUNREACH 146 | } 147 | return nil 148 | }, comm.Closer(headerCache.Release)) { 149 | return true 150 | } 151 | hdr.SetType(header.ICMPv6EchoReply) 152 | hdr.SetChecksum(0) 153 | hdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 154 | Header: hdr, 155 | Src: ipHdr.SourceAddress(), 156 | Dst: ipHdr.DestinationAddress(), 157 | })) 158 | t.writeBuffer(ipHdr) 159 | headerCache.Release() 160 | return false 161 | } 162 | -------------------------------------------------------------------------------- /nat/nat.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/buf" 7 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 8 | "golang.org/x/sys/unix" 9 | "gvisor.dev/gvisor/pkg/tcpip" 10 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 11 | "gvisor.dev/gvisor/pkg/tcpip/header" 12 | "gvisor.dev/gvisor/pkg/tcpip/header/parse" 13 | "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" 14 | "gvisor.dev/gvisor/pkg/tcpip/stack" 15 | "libcore/tun" 16 | ) 17 | 18 | //go:generate go run ../errorgen 19 | 20 | var _ tun.Tun = (*SystemTun)(nil) 21 | 22 | var ( 23 | vlanClient4 = tcpip.Address([]uint8{172, 19, 0, 1}) 24 | vlanClient6 = tcpip.Address([]uint8{0xfd, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}) 25 | ) 26 | 27 | type SystemTun struct { 28 | dev int 29 | mtu int 30 | handler tun.Handler 31 | ipv6Mode int32 32 | tcpForwarder *tcpForwarder 33 | errorHandler func(err string) 34 | } 35 | 36 | func New(dev int32, mtu int32, handler tun.Handler, ipv6Mode int32, errorHandler func(err string)) (*SystemTun, error) { 37 | t := &SystemTun{ 38 | dev: int(dev), 39 | mtu: int(mtu), 40 | handler: handler, 41 | ipv6Mode: ipv6Mode, 42 | errorHandler: errorHandler, 43 | } 44 | tcpServer, err := newTcpForwarder(t) 45 | if err != nil { 46 | return nil, err 47 | } 48 | go tcpServer.dispatchLoop() 49 | t.tcpForwarder = tcpServer 50 | 51 | go t.dispatchLoop() 52 | return t, nil 53 | } 54 | 55 | func (t *SystemTun) dispatchLoop() { 56 | cache := buf.NewSize(int32(t.mtu)) 57 | defer cache.Release() 58 | data := cache.Use() 59 | 60 | device := os.NewFile(uintptr(t.dev), "tun") 61 | element := v2rayNet.AddConnection(device) 62 | defer v2rayNet.RemoveConnection(element) 63 | 64 | for { 65 | n, err := device.Read(data) 66 | if err != nil { 67 | break 68 | } 69 | cache.Clear() 70 | cache.Resize(0, int32(n)) 71 | packet := data[:n] 72 | if t.deliverPacket(cache, packet) { 73 | cache = buf.New() 74 | data = cache.Extend(buf.Size) 75 | } 76 | } 77 | } 78 | 79 | func (t *SystemTun) writeRawPacket(vv buffer.VectorisedView) tcpip.Error { 80 | views := vv.Views() 81 | iovecs := make([]unix.Iovec, len(views)) 82 | for i, v := range views { 83 | iovecs[i] = rawfile.IovecFromBytes(v) 84 | } 85 | return rawfile.NonBlockingWriteIovec(t.dev, iovecs) 86 | } 87 | 88 | func (t *SystemTun) writeBuffer(bytes []byte) tcpip.Error { 89 | return rawfile.NonBlockingWrite(t.dev, bytes) 90 | } 91 | 92 | func (t *SystemTun) deliverPacket(cache *buf.Buffer, packet []byte) bool { 93 | switch header.IPVersion(packet) { 94 | case header.IPv4Version: 95 | ipHdr := header.IPv4(packet) 96 | switch ipHdr.TransportProtocol() { 97 | case header.TCPProtocolNumber: 98 | t.tcpForwarder.processIPv4(ipHdr, ipHdr.Payload()) 99 | case header.UDPProtocolNumber: 100 | t.processIPv4UDP(cache, ipHdr, ipHdr.Payload()) 101 | return true 102 | case header.ICMPv4ProtocolNumber: 103 | return t.processICMPv4(cache, ipHdr, ipHdr.Payload()) 104 | } 105 | case header.IPv6Version: 106 | pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ 107 | Data: buffer.View(packet).ToVectorisedView(), 108 | }) 109 | proto, _, _, _, ok := parse.IPv6(pkt) 110 | pkt.DecRef() 111 | if !ok { 112 | return false 113 | } 114 | ipHdr := header.IPv6(packet) 115 | switch proto { 116 | case header.TCPProtocolNumber: 117 | t.tcpForwarder.processIPv6(ipHdr, ipHdr.Payload()) 118 | case header.UDPProtocolNumber: 119 | t.processIPv6UDP(cache, ipHdr, ipHdr.Payload()) 120 | return true 121 | case header.ICMPv6ProtocolNumber: 122 | return t.processICMPv6(cache, ipHdr, ipHdr.Payload()) 123 | } 124 | } 125 | return false 126 | } 127 | 128 | func (t *SystemTun) Close() error { 129 | return t.tcpForwarder.Close() 130 | } 131 | -------------------------------------------------------------------------------- /nat/peer.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "gvisor.dev/gvisor/pkg/tcpip" 5 | ) 6 | 7 | type peerKey struct { 8 | destinationAddress tcpip.Address 9 | sourcePort uint16 10 | } 11 | 12 | type peerValue struct { 13 | sourceAddress tcpip.Address 14 | destinationPort uint16 15 | } 16 | -------------------------------------------------------------------------------- /nat/tcp.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "time" 7 | 8 | "github.com/Dreamacro/clash/common/cache" 9 | "github.com/sirupsen/logrus" 10 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 11 | "gvisor.dev/gvisor/pkg/tcpip" 12 | "gvisor.dev/gvisor/pkg/tcpip/header" 13 | "libcore/comm" 14 | ) 15 | 16 | type tcpForwarder struct { 17 | tun *SystemTun 18 | port uint16 19 | listener *net.TCPListener 20 | sessions *cache.LruCache 21 | } 22 | 23 | func newTcpForwarder(tun *SystemTun) (*tcpForwarder, error) { 24 | var network string 25 | address := &net.TCPAddr{} 26 | if tun.ipv6Mode == comm.IPv6Disable { 27 | network = "tcp4" 28 | address.IP = net.IP(vlanClient4) 29 | } else { 30 | network = "tcp" 31 | address.IP = net.IPv6zero 32 | } 33 | listener, err := net.ListenTCP(network, address) 34 | if err != nil { 35 | return nil, newError("failed to create tcp forwarder at ", address.IP).Base(err) 36 | } 37 | addr := listener.Addr().(*net.TCPAddr) 38 | port := uint16(addr.Port) 39 | newError("tcp forwarder started at ", addr).AtDebug().WriteToLog() 40 | return &tcpForwarder{tun, port, listener, cache.NewLRUCache( 41 | cache.WithAge(300), 42 | cache.WithUpdateAgeOnGet(), 43 | )}, nil 44 | } 45 | 46 | func (t *tcpForwarder) dispatch() (bool, error) { 47 | conn, err := t.listener.AcceptTCP() 48 | if err != nil { 49 | return true, err 50 | } 51 | addr := conn.RemoteAddr().(*net.TCPAddr) 52 | if ip4 := addr.IP.To4(); ip4 != nil { 53 | addr.IP = ip4 54 | } 55 | key := peerKey{tcpip.Address(addr.IP), uint16(addr.Port)} 56 | var session *peerValue 57 | iSession, ok := t.sessions.Get(peerKey{key.destinationAddress, key.sourcePort}) 58 | if ok { 59 | session = iSession.(*peerValue) 60 | } else { 61 | conn.Close() 62 | return false, newError("dropped unknown tcp session with source port ", key.sourcePort, " to destination address ", key.destinationAddress) 63 | } 64 | 65 | source := v2rayNet.Destination{ 66 | Address: v2rayNet.IPAddress([]byte(session.sourceAddress)), 67 | Port: v2rayNet.Port(key.sourcePort), 68 | Network: v2rayNet.Network_TCP, 69 | } 70 | destination := v2rayNet.Destination{ 71 | Address: v2rayNet.IPAddress([]byte(key.destinationAddress)), 72 | Port: v2rayNet.Port(session.destinationPort), 73 | Network: v2rayNet.Network_TCP, 74 | } 75 | 76 | go func() { 77 | t.tun.handler.NewConnection(source, destination, conn) 78 | time.Sleep(time.Second * 5) 79 | t.sessions.Delete(key) 80 | }() 81 | 82 | return false, nil 83 | } 84 | 85 | func (t *tcpForwarder) dispatchLoop() { 86 | for { 87 | stop, err := t.dispatch() 88 | if err != nil { 89 | e := newError("dispatch tcp conn failed").Base(err) 90 | e.WriteToLog() 91 | if stop { 92 | if !errors.Is(err, net.ErrClosed) { 93 | t.Close() 94 | t.tun.errorHandler(e.String()) 95 | } 96 | return 97 | } 98 | } 99 | } 100 | } 101 | 102 | func (t *tcpForwarder) processIPv4(ipHdr header.IPv4, tcpHdr header.TCP) { 103 | sourceAddress := ipHdr.SourceAddress() 104 | destinationAddress := ipHdr.DestinationAddress() 105 | sourcePort := tcpHdr.SourcePort() 106 | destinationPort := tcpHdr.DestinationPort() 107 | 108 | var session *peerValue 109 | 110 | if sourcePort != t.port { 111 | 112 | key := peerKey{destinationAddress, sourcePort} 113 | iSession, ok := t.sessions.Get(key) 114 | if ok { 115 | session = iSession.(*peerValue) 116 | } else { 117 | session = &peerValue{sourceAddress, destinationPort} 118 | t.sessions.Set(key, session) 119 | } 120 | 121 | ipHdr.SetSourceAddress(destinationAddress) 122 | ipHdr.SetDestinationAddress(vlanClient4) 123 | tcpHdr.SetDestinationPort(t.port) 124 | 125 | } else { 126 | 127 | iSession, ok := t.sessions.Get(peerKey{destinationAddress, destinationPort}) 128 | if ok { 129 | session = iSession.(*peerValue) 130 | } else { 131 | logrus.Warn("unknown tcp session with source port ", destinationPort, " to destination address ", destinationAddress) 132 | return 133 | } 134 | ipHdr.SetSourceAddress(destinationAddress) 135 | tcpHdr.SetSourcePort(session.destinationPort) 136 | ipHdr.SetDestinationAddress(session.sourceAddress) 137 | } 138 | 139 | ipHdr.SetChecksum(0) 140 | ipHdr.SetChecksum(^ipHdr.CalculateChecksum()) 141 | tcpHdr.SetChecksum(0) 142 | tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.ChecksumCombine( 143 | header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), uint16(len(tcpHdr))), 144 | header.Checksum(tcpHdr.Payload(), 0), 145 | ))) 146 | 147 | t.tun.writeBuffer(ipHdr) 148 | } 149 | 150 | func (t *tcpForwarder) processIPv6(ipHdr header.IPv6, tcpHdr header.TCP) { 151 | sourceAddress := ipHdr.SourceAddress() 152 | destinationAddress := ipHdr.DestinationAddress() 153 | sourcePort := tcpHdr.SourcePort() 154 | destinationPort := tcpHdr.DestinationPort() 155 | 156 | var session *peerValue 157 | 158 | if sourcePort != t.port { 159 | 160 | key := peerKey{destinationAddress, sourcePort} 161 | iSession, ok := t.sessions.Get(key) 162 | if ok { 163 | session = iSession.(*peerValue) 164 | } else { 165 | session = &peerValue{sourceAddress, destinationPort} 166 | t.sessions.Set(key, session) 167 | } 168 | 169 | ipHdr.SetSourceAddress(destinationAddress) 170 | ipHdr.SetDestinationAddress(vlanClient6) 171 | tcpHdr.SetDestinationPort(t.port) 172 | 173 | } else { 174 | 175 | iSession, ok := t.sessions.Get(peerKey{destinationAddress, destinationPort}) 176 | if ok { 177 | session = iSession.(*peerValue) 178 | } else { 179 | logrus.Warn("unknown tcp session with source port ", destinationPort, " to destination address ", destinationAddress) 180 | return 181 | } 182 | 183 | ipHdr.SetSourceAddress(destinationAddress) 184 | tcpHdr.SetSourcePort(session.destinationPort) 185 | ipHdr.SetDestinationAddress(session.sourceAddress) 186 | } 187 | 188 | tcpHdr.SetChecksum(0) 189 | tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.ChecksumCombine( 190 | header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), uint16(len(tcpHdr))), 191 | header.Checksum(tcpHdr.Payload(), 0), 192 | ))) 193 | 194 | t.tun.writeBuffer(ipHdr) 195 | } 196 | 197 | func (t *tcpForwarder) Close() error { 198 | return t.listener.Close() 199 | } 200 | -------------------------------------------------------------------------------- /nat/udp.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "github.com/v2fly/v2ray-core/v5/common/buf" 5 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 6 | "gvisor.dev/gvisor/pkg/tcpip" 7 | "gvisor.dev/gvisor/pkg/tcpip/buffer" 8 | "gvisor.dev/gvisor/pkg/tcpip/header" 9 | "libcore/comm" 10 | ) 11 | 12 | func (t *SystemTun) processIPv4UDP(cache *buf.Buffer, ipHdr header.IPv4, hdr header.UDP) { 13 | sourceAddress := ipHdr.SourceAddress() 14 | destinationAddress := ipHdr.DestinationAddress() 15 | sourcePort := hdr.SourcePort() 16 | destinationPort := hdr.DestinationPort() 17 | 18 | source := v2rayNet.Destination{ 19 | Address: v2rayNet.IPAddress([]byte(sourceAddress)), 20 | Port: v2rayNet.Port(sourcePort), 21 | Network: v2rayNet.Network_UDP, 22 | } 23 | destination := v2rayNet.Destination{ 24 | Address: v2rayNet.IPAddress([]byte(destinationAddress)), 25 | Port: v2rayNet.Port(destinationPort), 26 | Network: v2rayNet.Network_UDP, 27 | } 28 | 29 | ipHdr.SetDestinationAddress(sourceAddress) 30 | hdr.SetDestinationPort(sourcePort) 31 | 32 | headerLength := ipHdr.HeaderLength() 33 | headerCache := buf.New() 34 | headerCache.Write(ipHdr[:headerLength+header.UDPMinimumSize]) 35 | 36 | cache.Advance(int32(headerLength + header.UDPMinimumSize)) 37 | go t.handler.NewPacket(source, destination, cache, func(bytes []byte, addr *v2rayNet.UDPAddr) (int, error) { 38 | index := headerCache.Len() 39 | newHeader := headerCache.ExtendCopy(headerCache.Bytes()) 40 | headerCache.Advance(index) 41 | 42 | defer func() { 43 | headerCache.Clear() 44 | headerCache.Resize(0, index) 45 | }() 46 | 47 | var newSourceAddress tcpip.Address 48 | var newSourcePort uint16 49 | 50 | if addr != nil { 51 | newSourceAddress = tcpip.Address(addr.IP) 52 | newSourcePort = uint16(addr.Port) 53 | } else { 54 | newSourceAddress = destinationAddress 55 | newSourcePort = destinationPort 56 | } 57 | 58 | newIpHdr := header.IPv4(newHeader) 59 | newIpHdr.SetSourceAddress(newSourceAddress) 60 | newIpHdr.SetTotalLength(uint16(int(headerCache.Len()) + len(bytes))) 61 | newIpHdr.SetChecksum(0) 62 | newIpHdr.SetChecksum(^newIpHdr.CalculateChecksum()) 63 | 64 | udpHdr := header.UDP(headerCache.BytesFrom(headerCache.Len() - header.UDPMinimumSize)) 65 | udpHdr.SetSourcePort(newSourcePort) 66 | udpHdr.SetLength(uint16(header.UDPMinimumSize + len(bytes))) 67 | udpHdr.SetChecksum(0) 68 | udpHdr.SetChecksum(^udpHdr.CalculateChecksum(header.Checksum(bytes, header.PseudoHeaderChecksum(header.UDPProtocolNumber, newSourceAddress, sourceAddress, uint16(header.UDPMinimumSize+len(bytes)))))) 69 | 70 | replyVV := buffer.VectorisedView{} 71 | replyVV.AppendView(newHeader) 72 | replyVV.AppendView(bytes) 73 | 74 | if err := t.writeRawPacket(replyVV); err != nil { 75 | return 0, newError(err.String()) 76 | } 77 | 78 | return len(bytes), nil 79 | }, comm.Closer(headerCache.Release)) 80 | } 81 | 82 | func (t *SystemTun) processIPv6UDP(cache *buf.Buffer, ipHdr header.IPv6, hdr header.UDP) { 83 | sourceAddress := ipHdr.SourceAddress() 84 | destinationAddress := ipHdr.DestinationAddress() 85 | sourcePort := hdr.SourcePort() 86 | destinationPort := hdr.DestinationPort() 87 | 88 | source := v2rayNet.Destination{ 89 | Address: v2rayNet.IPAddress([]byte(sourceAddress)), 90 | Port: v2rayNet.Port(sourcePort), 91 | Network: v2rayNet.Network_UDP, 92 | } 93 | destination := v2rayNet.Destination{ 94 | Address: v2rayNet.IPAddress([]byte(destinationAddress)), 95 | Port: v2rayNet.Port(destinationPort), 96 | Network: v2rayNet.Network_UDP, 97 | } 98 | 99 | ipHdr.SetDestinationAddress(sourceAddress) 100 | hdr.SetDestinationPort(sourcePort) 101 | 102 | headerLength := uint16(len(ipHdr)) - ipHdr.PayloadLength() 103 | headerCache := buf.New() 104 | headerCache.Write(ipHdr[:headerLength+header.UDPMinimumSize]) 105 | 106 | cache.Advance(int32(headerLength + header.UDPMinimumSize)) 107 | go t.handler.NewPacket(source, destination, cache, func(bytes []byte, addr *v2rayNet.UDPAddr) (int, error) { 108 | index := headerCache.Len() 109 | newHeader := headerCache.ExtendCopy(headerCache.Bytes()) 110 | headerCache.Advance(index) 111 | 112 | defer func() { 113 | headerCache.Clear() 114 | headerCache.Resize(0, index) 115 | }() 116 | 117 | var newSourceAddress tcpip.Address 118 | var newSourcePort uint16 119 | 120 | if addr != nil { 121 | newSourceAddress = tcpip.Address(addr.IP) 122 | newSourcePort = uint16(addr.Port) 123 | } else { 124 | newSourceAddress = destinationAddress 125 | newSourcePort = destinationPort 126 | } 127 | 128 | newIpHdr := header.IPv6(newHeader) 129 | newIpHdr.SetSourceAddress(newSourceAddress) 130 | newIpHdr.SetPayloadLength(uint16(header.UDPMinimumSize + len(bytes))) 131 | 132 | udpHdr := header.UDP(headerCache.BytesFrom(headerCache.Len() - header.UDPMinimumSize)) 133 | udpHdr.SetSourcePort(newSourcePort) 134 | udpHdr.SetLength(uint16(header.UDPMinimumSize + len(bytes))) 135 | udpHdr.SetChecksum(0) 136 | udpHdr.SetChecksum(^udpHdr.CalculateChecksum(header.Checksum(bytes, header.PseudoHeaderChecksum(header.UDPProtocolNumber, newSourceAddress, sourceAddress, uint16(header.UDPMinimumSize+len(bytes)))))) 137 | 138 | replyVV := buffer.VectorisedView{} 139 | replyVV.AppendView(headerCache.Bytes()) 140 | replyVV.AppendView(bytes) 141 | 142 | if err := t.writeRawPacket(replyVV); err != nil { 143 | return 0, newError(err.String()) 144 | } 145 | 146 | return len(bytes), nil 147 | }, comm.Closer(headerCache.Release)) 148 | } 149 | -------------------------------------------------------------------------------- /obfs.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Dreamacro/clash/transport/simple-obfs" 7 | "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" 8 | "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks/plugin/self" 9 | "github.com/v2fly/v2ray-core/v5/transport/internet" 10 | ) 11 | 12 | var _ shadowsocks.StreamPlugin = (*obfsLocalPlugin)(nil) 13 | 14 | func init() { 15 | shadowsocks.RegisterPlugin("obfs-local", func() shadowsocks.SIP003Plugin { 16 | return new(obfsLocalPlugin) 17 | }) 18 | } 19 | 20 | type obfsLocalPlugin struct { 21 | tls bool 22 | host string 23 | port string 24 | } 25 | 26 | func (p *obfsLocalPlugin) Init(_ context.Context, _ string, _ string, _ string, remotePort string, pluginOpts string, _ []string, _ *shadowsocks.MemoryAccount) error { 27 | options, err := self.ParsePluginOptions(pluginOpts) 28 | if err != nil { 29 | return newError("obfs-local: failed to parse plugin options").Base(err) 30 | } 31 | 32 | mode := "http" 33 | 34 | if s, ok := options.Get("obfs"); ok { 35 | mode = s 36 | } 37 | 38 | if s, ok := options.Get("obfs-host"); ok { 39 | p.host = s 40 | } 41 | 42 | switch mode { 43 | case "http": 44 | case "tls": 45 | p.tls = true 46 | default: 47 | return newError("unknown obfs mode ", mode) 48 | } 49 | 50 | p.port = remotePort 51 | 52 | return nil 53 | } 54 | 55 | func (p *obfsLocalPlugin) StreamConn(connection internet.Connection) internet.Connection { 56 | if !p.tls { 57 | return obfs.NewHTTPObfs(connection, p.host, p.port) 58 | } else { 59 | return obfs.NewTLSObfs(connection, p.host) 60 | } 61 | } 62 | 63 | func (p *obfsLocalPlugin) Close() error { 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /observatory.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "github.com/sirupsen/logrus" 6 | "github.com/v2fly/v2ray-core/v5/app/observatory" 7 | "github.com/v2fly/v2ray-core/v5/features/extension" 8 | ) 9 | 10 | func (instance *V2RayInstance) GetObservatoryStatus(tag string) ([]byte, error) { 11 | if instance.observatory == nil { 12 | return nil, newError("observatory unavailable") 13 | } 14 | observer, err := instance.observatory.GetFeaturesByTag(tag) 15 | if err != nil { 16 | return nil, err 17 | } 18 | status, err := observer.(extension.Observatory).GetObservation(nil) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return proto.Marshal(status) 23 | } 24 | 25 | func (instance *V2RayInstance) UpdateStatus(tag string, status []byte) error { 26 | if instance.observatory == nil { 27 | return newError("observatory unavailable") 28 | } 29 | 30 | s := new(observatory.OutboundStatus) 31 | err := proto.Unmarshal(status, s) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | observer, err := instance.observatory.GetFeaturesByTag(tag) 37 | if err != nil { 38 | return err 39 | } 40 | observer.(*observatory.Observer).UpdateStatus(s) 41 | return err 42 | } 43 | 44 | type ObservatoryStatusUpdateListener interface { 45 | OnUpdateObservatoryStatus(status []byte) error 46 | } 47 | 48 | func (instance *V2RayInstance) SetStatusUpdateListener(tag string, listener ObservatoryStatusUpdateListener) error { 49 | if listener == nil { 50 | observer, err := instance.observatory.GetFeaturesByTag(tag) 51 | if err != nil { 52 | return err 53 | } 54 | observer.(*observatory.Observer).StatusUpdate = nil 55 | } else { 56 | observer, err := instance.observatory.GetFeaturesByTag(tag) 57 | if err != nil { 58 | return err 59 | } 60 | observer.(*observatory.Observer).StatusUpdate = func(result *observatory.OutboundStatus) { 61 | status, _ := proto.Marshal(result) 62 | err = listener.OnUpdateObservatoryStatus(status) 63 | if err != nil { 64 | logrus.Warn("failed to send observatory status update: ", err) 65 | } 66 | } 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /procfs.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "unsafe" 12 | 13 | "github.com/v2fly/v2ray-core/v5/common/net" 14 | ) 15 | 16 | var ( 17 | netIndexOfLocal = -1 18 | netIndexOfUid = -1 19 | nativeEndian binary.ByteOrder 20 | ) 21 | 22 | func querySocketUidFromProcFs(source, _ net.Destination) int32 { 23 | if netIndexOfLocal < 0 || netIndexOfUid < 0 { 24 | return -1 25 | } 26 | 27 | path := "/proc/net/" 28 | 29 | if source.Network == net.Network_TCP { 30 | path += "tcp" 31 | } else { 32 | path += "udp" 33 | } 34 | 35 | if source.Address.Family().IsIPv6() { 36 | path += "6" 37 | } 38 | 39 | sIP := source.Address.IP() 40 | if len(sIP) == 0 { 41 | return -1 42 | } 43 | 44 | var bytes [2]byte 45 | binary.BigEndian.PutUint16(bytes[:], uint16(source.Port)) 46 | local := fmt.Sprintf("%s:%s", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:])) 47 | 48 | file, err := os.Open(path) 49 | if err != nil { 50 | return -1 51 | } 52 | 53 | defer file.Close() 54 | 55 | reader := bufio.NewReader(file) 56 | 57 | for { 58 | row, _, err := reader.ReadLine() 59 | if err != nil { 60 | return -1 61 | } 62 | 63 | fields := strings.Fields(string(row)) 64 | 65 | if len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid { 66 | continue 67 | } 68 | 69 | if strings.EqualFold(local, fields[netIndexOfLocal]) { 70 | uid, err := strconv.Atoi(fields[netIndexOfUid]) 71 | if err != nil { 72 | return -1 73 | } 74 | 75 | return int32(uid) 76 | } 77 | } 78 | } 79 | 80 | func nativeEndianIP(ip net.IP) []byte { 81 | result := make([]byte, len(ip)) 82 | 83 | for i := 0; i < len(ip); i += 4 { 84 | value := binary.BigEndian.Uint32(ip[i:]) 85 | 86 | nativeEndian.PutUint32(result[i:], value) 87 | } 88 | 89 | return result 90 | } 91 | 92 | func init() { 93 | file, err := os.Open("/proc/net/tcp") 94 | if err != nil { 95 | return 96 | } 97 | 98 | defer file.Close() 99 | 100 | reader := bufio.NewReader(file) 101 | 102 | header, _, err := reader.ReadLine() 103 | if err != nil { 104 | return 105 | } 106 | 107 | columns := strings.Fields(string(header)) 108 | 109 | var txQueue, rxQueue, tr, tmWhen bool 110 | 111 | for idx, col := range columns { 112 | offset := 0 113 | 114 | if txQueue && rxQueue { 115 | offset-- 116 | } 117 | 118 | if tr && tmWhen { 119 | offset-- 120 | } 121 | 122 | switch col { 123 | case "tx_queue": 124 | txQueue = true 125 | case "rx_queue": 126 | rxQueue = true 127 | case "tr": 128 | tr = true 129 | case "tm->when": 130 | tmWhen = true 131 | case "local_address": 132 | netIndexOfLocal = idx + offset 133 | case "uid": 134 | netIndexOfUid = idx + offset 135 | } 136 | } 137 | } 138 | 139 | func init() { 140 | var x uint32 = 0x01020304 141 | if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 142 | nativeEndian = binary.BigEndian 143 | } else { 144 | nativeEndian = binary.LittleEndian 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /protect.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "os" 9 | 10 | "github.com/sirupsen/logrus" 11 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 12 | "github.com/v2fly/v2ray-core/v5/features/dns" 13 | "github.com/v2fly/v2ray-core/v5/transport/internet" 14 | "golang.org/x/sys/unix" 15 | ) 16 | 17 | type Protector interface { 18 | Protect(fd int32) bool 19 | } 20 | 21 | var noopProtectorInstance = &noopProtector{} 22 | 23 | type noopProtector struct{} 24 | 25 | func (n *noopProtector) Protect(int32) bool { 26 | return true 27 | } 28 | 29 | type protectedDialer struct { 30 | protector Protector 31 | resolver func(ctx context.Context, domain string) ([]net.IP, error) 32 | } 33 | 34 | func (dialer protectedDialer) Dial(ctx context.Context, source v2rayNet.Address, destination v2rayNet.Destination, sockopt *internet.SocketConfig) (conn net.Conn, err error) { 35 | if destination.Network == v2rayNet.Network_Unknown || destination.Address == nil { 36 | panic("connect to invalid destination") 37 | } 38 | 39 | var ips []net.IP 40 | if destination.Address.Family().IsDomain() { 41 | ips, err = dialer.resolver(ctx, destination.Address.Domain()) 42 | if err == nil && len(ips) == 0 { 43 | err = dns.ErrEmptyResponse 44 | } 45 | if err != nil { 46 | return nil, err 47 | } 48 | } else { 49 | ips = append(ips, destination.Address.IP()) 50 | } 51 | 52 | for i, ip := range ips { 53 | if i > 0 { 54 | if err == nil { 55 | break 56 | } else { 57 | logrus.Warn("dial system failed: ", err) 58 | } 59 | logrus.Debug("trying next address: ", ip.String()) 60 | } 61 | destination.Address = v2rayNet.IPAddress(ip) 62 | conn, err = dialer.dial(ctx, source, destination, sockopt) 63 | } 64 | 65 | return conn, err 66 | } 67 | 68 | func (dialer protectedDialer) dial(ctx context.Context, source v2rayNet.Address, destination v2rayNet.Destination, sockopt *internet.SocketConfig) (conn net.Conn, err error) { 69 | destIp := destination.Address.IP() 70 | fd, err := getFd(destination) 71 | if err != nil { 72 | return 73 | } 74 | 75 | if !dialer.protector.Protect(int32(fd)) { 76 | unix.Close(fd) 77 | return nil, errors.New("protect failed") 78 | } 79 | 80 | if sockopt != nil { 81 | internet.ApplySockopt(sockopt, destination, uintptr(fd), ctx) 82 | } 83 | 84 | switch destination.Network { 85 | case v2rayNet.Network_TCP: 86 | var sockaddr unix.Sockaddr 87 | if destination.Address.Family().IsIPv4() { 88 | socketAddress := &unix.SockaddrInet4{ 89 | Port: int(destination.Port), 90 | } 91 | copy(socketAddress.Addr[:], destIp) 92 | sockaddr = socketAddress 93 | } else { 94 | socketAddress := &unix.SockaddrInet6{ 95 | Port: int(destination.Port), 96 | } 97 | copy(socketAddress.Addr[:], destIp) 98 | sockaddr = socketAddress 99 | } 100 | 101 | err = unix.Connect(fd, sockaddr) 102 | case v2rayNet.Network_UDP: 103 | err = unix.Bind(fd, &unix.SockaddrInet6{}) 104 | } 105 | if err != nil { 106 | unix.Close(fd) 107 | return 108 | } 109 | 110 | file := os.NewFile(uintptr(fd), "socket") 111 | if file == nil { 112 | return nil, errors.New("failed to connect to fd") 113 | } 114 | defer file.Close() 115 | 116 | switch destination.Network { 117 | case v2rayNet.Network_UDP: 118 | pc, err := net.FilePacketConn(file) 119 | if err == nil { 120 | destAddr, err := net.ResolveUDPAddr("udp", destination.NetAddr()) 121 | if err != nil { 122 | return nil, err 123 | } 124 | conn = &internet.PacketConnWrapper{ 125 | Conn: pc, 126 | Dest: destAddr, 127 | } 128 | } 129 | default: 130 | conn, err = net.FileConn(file) 131 | } 132 | 133 | return 134 | } 135 | 136 | func getFd(destination v2rayNet.Destination) (fd int, err error) { 137 | var af int 138 | if destination.Network == v2rayNet.Network_TCP && destination.Address.Family().IsIPv4() { 139 | af = unix.AF_INET 140 | } else { 141 | af = unix.AF_INET6 142 | } 143 | switch destination.Network { 144 | case v2rayNet.Network_TCP: 145 | fd, err = unix.Socket(af, unix.SOCK_STREAM, unix.IPPROTO_TCP) 146 | case v2rayNet.Network_UDP: 147 | fd, err = unix.Socket(af, unix.SOCK_DGRAM, unix.IPPROTO_UDP) 148 | case v2rayNet.Network_UNIX: 149 | fd, err = unix.Socket(af, unix.SOCK_STREAM, 0) 150 | default: 151 | err = fmt.Errorf("unknow network") 152 | } 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /route.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import "github.com/sirupsen/logrus" 4 | 5 | var networkType string 6 | 7 | func SetNetworkType(network string) { 8 | if network != networkType { 9 | logrus.Debug("updated network type: ", network) 10 | networkType = network 11 | } 12 | } 13 | 14 | var wifiSSID string 15 | 16 | func SetWifiSSID(ssid string) { 17 | if ssid != wifiSSID { 18 | logrus.Debug("updated wifi ssid: ", ssid) 19 | wifiSSID = ssid 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ssr.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "flag" 7 | "strconv" 8 | 9 | "github.com/Dreamacro/clash/transport/ssr/obfs" 10 | "github.com/Dreamacro/clash/transport/ssr/protocol" 11 | "github.com/v2fly/v2ray-core/v5/common/buf" 12 | "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" 13 | "github.com/v2fly/v2ray-core/v5/transport/internet" 14 | ) 15 | 16 | var ( 17 | _ shadowsocks.SIP003Plugin = (*shadowsocksrPlugin)(nil) 18 | _ shadowsocks.StreamPlugin = (*shadowsocksrPlugin)(nil) 19 | _ shadowsocks.ProtocolPlugin = (*shadowsocksrPlugin)(nil) 20 | ) 21 | 22 | func init() { 23 | shadowsocks.RegisterPlugin("shadowsocksr", func() shadowsocks.SIP003Plugin { 24 | return new(shadowsocksrPlugin) 25 | }) 26 | } 27 | 28 | type shadowsocksrPlugin struct { 29 | host string 30 | port int 31 | obfs string 32 | obfsParam string 33 | protocol string 34 | protocolParam string 35 | 36 | o obfs.Obfs 37 | p protocol.Protocol 38 | } 39 | 40 | func (p *shadowsocksrPlugin) Init(_ context.Context, _ string, _ string, remoteHost string, remotePort string, _ string, pluginArgs []string, account *shadowsocks.MemoryAccount) error { 41 | fs := flag.NewFlagSet("shadowsocksr", flag.ContinueOnError) 42 | fs.StringVar(&p.obfs, "obfs", "origin", "") 43 | fs.StringVar(&p.obfsParam, "obfs-param", "", "") 44 | fs.StringVar(&p.protocol, "protocol", "origin", "") 45 | fs.StringVar(&p.protocolParam, "protocol-param", "", "") 46 | if err := fs.Parse(pluginArgs); err != nil { 47 | return newError("shadowsocksr: failed to parse args").Base(err) 48 | } 49 | p.host = remoteHost 50 | p.port, _ = strconv.Atoi(remotePort) 51 | 52 | obfs, obfsOverhead, err := obfs.PickObfs(p.obfs, &obfs.Base{ 53 | Host: p.host, 54 | Port: p.port, 55 | Key: account.Key, 56 | IVSize: int(account.Cipher.IVSize()), 57 | Param: p.obfsParam, 58 | }) 59 | if err != nil { 60 | return newError("failed to create ssr obfs").Base(err) 61 | } 62 | 63 | protocol, err := protocol.PickProtocol(p.protocol, &protocol.Base{ 64 | Key: account.Key, 65 | Overhead: obfsOverhead, 66 | Param: p.protocolParam, 67 | }) 68 | if err != nil { 69 | return newError("failed to create ssr protocol").Base(err) 70 | } 71 | 72 | p.o = obfs 73 | p.p = protocol 74 | 75 | return nil 76 | } 77 | 78 | func (p *shadowsocksrPlugin) Close() error { 79 | return nil 80 | } 81 | 82 | func (p *shadowsocksrPlugin) StreamConn(conn internet.Connection) internet.Connection { 83 | return p.o.StreamConn(conn) 84 | } 85 | 86 | func (p *shadowsocksrPlugin) ProtocolConn(conn *shadowsocks.ProtocolConn, iv []byte) { 87 | upstream := buf.NewConnection(buf.ConnectionOutputMulti(conn), buf.ConnectionInputMulti(conn)) 88 | downstream := p.p.StreamConn(upstream, iv) 89 | if upstream == downstream { 90 | conn.ProtocolReader = conn 91 | conn.ProtocolWriter = conn 92 | } else { 93 | conn.ProtocolReader = buf.NewReader(downstream) 94 | conn.ProtocolWriter = buf.NewWriter(downstream) 95 | } 96 | } 97 | 98 | func (p *shadowsocksrPlugin) EncodePacket(buffer *buf.Buffer, ivLen int32) (*buf.Buffer, error) { 99 | defer buffer.Release() 100 | packet := &bytes.Buffer{} 101 | err := p.p.EncodePacket(packet, buffer.BytesFrom(ivLen)) 102 | if err != nil { 103 | return nil, err 104 | } 105 | if ivLen > 0 { 106 | newBuffer := buf.New() 107 | newBuffer.Write(buffer.BytesTo(ivLen)) 108 | newBuffer.Write(packet.Bytes()) 109 | return newBuffer, nil 110 | } else { 111 | return buf.FromBytes(packet.Bytes()), nil 112 | } 113 | } 114 | 115 | func (p *shadowsocksrPlugin) DecodePacket(buffer *buf.Buffer) (*buf.Buffer, error) { 116 | defer buffer.Release() 117 | packet, err := p.p.DecodePacket(buffer.Bytes()) 118 | if err != nil { 119 | return nil, err 120 | } 121 | newBuffer := buf.New() 122 | newBuffer.Write(packet) 123 | newBuffer.Endpoint = buffer.Endpoint 124 | return newBuffer, nil 125 | } 126 | -------------------------------------------------------------------------------- /stats.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "container/list" 5 | "net" 6 | "sync" 7 | "sync/atomic" 8 | 9 | "github.com/v2fly/v2ray-core/v5/common" 10 | "github.com/v2fly/v2ray-core/v5/common/buf" 11 | "github.com/v2fly/v2ray-core/v5/transport/internet" 12 | ) 13 | 14 | type AppStats struct { 15 | Uid int32 16 | TcpConn int32 17 | UdpConn int32 18 | TcpConnTotal int32 19 | UdpConnTotal int32 20 | 21 | Uplink int64 22 | Downlink int64 23 | UplinkTotal int64 24 | DownlinkTotal int64 25 | 26 | DeactivateAt int32 27 | } 28 | 29 | type appStats struct { 30 | sync.Mutex 31 | 32 | tcpConn int32 33 | udpConn int32 34 | tcpConnTotal uint32 35 | udpConnTotal uint32 36 | 37 | uplink uint64 38 | downlink uint64 39 | uplinkTotal uint64 40 | downlinkTotal uint64 41 | 42 | deactivateAt int64 43 | 44 | connections list.List 45 | } 46 | 47 | type TrafficListener interface { 48 | UpdateStats(t *AppStats) 49 | } 50 | 51 | func (t *Tun2ray) GetTrafficStatsEnabled() bool { 52 | return t.trafficStats 53 | } 54 | 55 | func (t *Tun2ray) ResetAppTraffics() { 56 | if !t.trafficStats { 57 | return 58 | } 59 | 60 | var toDel []uint16 61 | t.appStats.Range(func(key, value interface{}) bool { 62 | uid := key.(uint16) 63 | toDel = append(toDel, uid) 64 | return true 65 | }) 66 | for _, uid := range toDel { 67 | t.appStats.Delete(uid) 68 | } 69 | } 70 | 71 | func (t *Tun2ray) CloseConnections(uid int32) { 72 | if !t.trafficStats { 73 | return 74 | } 75 | statsI, ok := t.appStats.Load(uint16(uid)) 76 | if !ok { 77 | return 78 | } 79 | stats := statsI.(*appStats) 80 | stats.Lock() 81 | defer stats.Unlock() 82 | for element := stats.connections.Front(); element != nil; element = element.Next() { 83 | common.Close(element.Value) 84 | } 85 | } 86 | 87 | func (t *Tun2ray) ReadAppTraffics(listener TrafficListener) error { 88 | if !t.trafficStats { 89 | return nil 90 | } 91 | 92 | var stats []*AppStats 93 | 94 | t.appStats.Range(func(key, value interface{}) bool { 95 | uid := key.(uint16) 96 | stat := value.(*appStats) 97 | export := &AppStats{ 98 | Uid: int32(uid), 99 | TcpConn: stat.tcpConn, 100 | UdpConn: stat.udpConn, 101 | TcpConnTotal: int32(stat.tcpConnTotal), 102 | UdpConnTotal: int32(stat.udpConnTotal), 103 | DeactivateAt: int32(stat.deactivateAt), 104 | } 105 | 106 | uplink := atomic.SwapUint64(&stat.uplink, 0) 107 | uplinkTotal := atomic.AddUint64(&stat.uplinkTotal, uplink) 108 | export.Uplink = int64(uplink) 109 | export.UplinkTotal = int64(uplinkTotal) 110 | 111 | downlink := atomic.SwapUint64(&stat.downlink, 0) 112 | downlinkTotal := atomic.AddUint64(&stat.downlinkTotal, downlink) 113 | export.Downlink = int64(downlink) 114 | export.DownlinkTotal = int64(downlinkTotal) 115 | 116 | stats = append(stats, export) 117 | return true 118 | }) 119 | 120 | for _, stat := range stats { 121 | listener.UpdateStats(stat) 122 | } 123 | 124 | return nil 125 | } 126 | 127 | func NewStatsCounterConn(originConn net.Conn, uplink *uint64, downlink *uint64) *internet.StatCounterConn { 128 | conn := new(internet.StatCounterConn) 129 | conn.Connection = originConn 130 | conn.ReadCounter = statsConnWrapper{uplink} 131 | conn.WriteCounter = statsConnWrapper{downlink} 132 | return conn 133 | } 134 | 135 | type statsConnWrapper struct { 136 | counter *uint64 137 | } 138 | 139 | func (w statsConnWrapper) Value() int64 { 140 | return int64(atomic.LoadUint64(w.counter)) 141 | } 142 | 143 | func (w statsConnWrapper) Set(i int64) int64 { 144 | value := w.Value() 145 | atomic.StoreUint64(w.counter, uint64(i)) 146 | return value 147 | } 148 | 149 | func (w statsConnWrapper) Add(i int64) int64 { 150 | atomic.AddUint64(w.counter, uint64(i)) 151 | return 0 152 | } 153 | 154 | type statsPacketConn struct { 155 | packetConn 156 | uplink *uint64 157 | downlink *uint64 158 | } 159 | 160 | func (c statsPacketConn) readFrom() (buffer *buf.Buffer, addr net.Addr, err error) { 161 | buffer, addr, err = c.packetConn.readFrom() 162 | if err == nil { 163 | atomic.AddUint64(c.downlink, uint64(buffer.Len())) 164 | } 165 | return 166 | } 167 | 168 | func (c statsPacketConn) writeTo(buffer *buf.Buffer, addr net.Addr) (err error) { 169 | length := buffer.Len() 170 | err = c.packetConn.writeTo(buffer, addr) 171 | if err == nil { 172 | atomic.AddUint64(c.uplink, uint64(length)) 173 | } 174 | return 175 | } 176 | -------------------------------------------------------------------------------- /stun/errors.generated.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/errors" 7 | ) 8 | 9 | type errPathObjHolder struct{} 10 | 11 | func newError(values ...interface{}) *errors.Error { 12 | return errors.New(values...).WithPathObj(errPathObjHolder{}) 13 | } 14 | 15 | func newErrorf(format string, a ...interface{}) *errors.Error { 16 | return errors.New(fmt.Sprintf(format, a)).WithPathObj(errPathObjHolder{}) 17 | } 18 | -------------------------------------------------------------------------------- /stun/socks.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/transport/socks5" 7 | ) 8 | 9 | type socksPacketConn struct { 10 | *net.UDPConn 11 | tcpConn net.Conn 12 | } 13 | 14 | func (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { 15 | packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b) 16 | if err != nil { 17 | return 18 | } 19 | return uc.UDPConn.Write(packet) 20 | } 21 | 22 | func (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 23 | _, _, err := uc.UDPConn.ReadFrom(b) 24 | if err != nil { 25 | return 0, nil, err 26 | } 27 | addr, payload, err := socks5.DecodeUDPPacket(b) 28 | if err != nil { 29 | return 0, nil, err 30 | } 31 | 32 | udpAddr := addr.UDPAddr() 33 | if udpAddr == nil { 34 | return 0, nil, newError("parse udp addr error") 35 | } 36 | 37 | // due to DecodeUDPPacket is mutable, record addr length 38 | copy(b, payload) 39 | return len(payload), udpAddr, nil 40 | } 41 | 42 | func (uc *socksPacketConn) Close() error { 43 | uc.tcpConn.Close() 44 | return uc.UDPConn.Close() 45 | } 46 | -------------------------------------------------------------------------------- /stun/stun.go: -------------------------------------------------------------------------------- 1 | package stun 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "time" 8 | 9 | "github.com/Dreamacro/clash/transport/socks5" 10 | "github.com/pion/stun" 11 | "github.com/sirupsen/logrus" 12 | "github.com/v2fly/v2ray-core/v5/common/buf" 13 | ) 14 | 15 | //go:generate go run ../errorgen 16 | 17 | type stunServerConn struct { 18 | conn net.PacketConn 19 | LocalAddr net.Addr 20 | RemoteAddr *net.UDPAddr 21 | OtherAddr *net.UDPAddr 22 | messageChan chan *stunResponse 23 | } 24 | 25 | type stunResponse struct { 26 | *stun.Message 27 | net.Addr 28 | } 29 | 30 | func (c *stunServerConn) Close() error { 31 | return c.conn.Close() 32 | } 33 | 34 | var timeout = 5 35 | 36 | const ( 37 | messageHeaderSize = 20 38 | ) 39 | 40 | const ( 41 | NoResult = iota 42 | EndpointIndependentNoNAT 43 | EndpointIndependent 44 | AddressDependent 45 | AddressAndPortDependent 46 | ) 47 | 48 | var ( 49 | errResponseMessage = errors.New("error reading from response message channel") 50 | errTimedOut = errors.New("timed out waiting for response") 51 | errNoOtherAddress = errors.New("no OTHER-ADDRESS in message") 52 | ) 53 | 54 | func Test(addrStr string, socksPort int) (natMapping int, natFiltering int, err error) { 55 | if addrStr == "" { 56 | addrStr = "stun.syncthing.net:3478" 57 | } 58 | var mapTestConn *stunServerConn 59 | newConn := func() error { 60 | if err == nil { 61 | mapTestConn, err = connect(addrStr, socksPort) 62 | if err != nil { 63 | e := newError("error creating STUN connection").Base(err) 64 | logrus.Warn(e) 65 | return e 66 | } 67 | } 68 | return err 69 | } 70 | if newConn() == nil { 71 | natMapping, err = mappingTests(mapTestConn) 72 | } 73 | if newConn() == nil { 74 | natFiltering, err = filteringTests(mapTestConn) 75 | } 76 | return 77 | } 78 | 79 | // RFC5780: 4.3. Determining NAT Mapping Behavior 80 | func mappingTests(mapTestConn *stunServerConn) (int, error) { 81 | defer mapTestConn.Close() 82 | // Test I: Regular binding request 83 | logrus.Info(newError("mapping test I: regular binding request")) 84 | request := stun.MustBuild(stun.TransactionID, stun.BindingRequest) 85 | 86 | resp, err := mapTestConn.roundTrip(request, mapTestConn.RemoteAddr) 87 | if err != nil { 88 | return NoResult, err 89 | } 90 | 91 | // Parse response message for XOR-MAPPED-ADDRESS and make sure OTHER-ADDRESS valid 92 | resps := parse(resp.Message) 93 | if resps.xorAddr == nil || resps.otherAddr == nil { 94 | err := newError("NAT discovery feature not supported by this server").Base(errNoOtherAddress) 95 | logrus.Warn(err) 96 | return NoResult, err 97 | } 98 | addr, err := net.ResolveUDPAddr("udp4", resps.otherAddr.String()) 99 | if err != nil { 100 | err := newError("failed resolving OTHER-ADDRESS: ", resps.otherAddr) 101 | logrus.Warn(err) 102 | return NoResult, err 103 | } 104 | mapTestConn.OtherAddr = addr 105 | logrus.Info(newError("received XOR-MAPPED-ADDRESS: ", resps.xorAddr)) 106 | 107 | // Assert mapping behavior 108 | if resps.xorAddr.String() == mapTestConn.LocalAddr.String() { 109 | logrus.Info(newError("NAT mapping behavior: endpoint independent (no NAT)")) 110 | return EndpointIndependentNoNAT, err 111 | } 112 | 113 | // Test II: Send binding request to the other address but primary port 114 | logrus.Info(newError("mapping test II: Send binding request to the other address but primary port")) 115 | oaddr := *mapTestConn.OtherAddr 116 | oaddr.Port = mapTestConn.RemoteAddr.Port 117 | resp, err = mapTestConn.roundTrip(request, &oaddr) 118 | if err != nil { 119 | if !errors.Is(err, errTimedOut) { 120 | return NoResult, err 121 | } 122 | } else { 123 | // Assert mapping behavior 124 | resps2 := parse(resp.Message) 125 | 126 | if resps2.respOrigin.String() != oaddr.String() { 127 | return AddressAndPortDependent, nil 128 | } 129 | 130 | logrus.Info(newError("received XOR-MAPPED-ADDRESS: ", resps2.xorAddr)) 131 | if resps2.xorAddr.String() == resps.xorAddr.String() { 132 | logrus.Info(newError("NAT mapping behavior: endpoint independent")) 133 | return EndpointIndependent, nil 134 | } 135 | 136 | resps = resps2 137 | } 138 | 139 | // Test III: Send binding request to the other address and port 140 | logrus.Info(newError("mapping test III: Send binding request to the other address and port")) 141 | resp, err = mapTestConn.roundTrip(request, mapTestConn.OtherAddr) 142 | if err != nil { 143 | if !errors.Is(err, errTimedOut) { 144 | return NoResult, err 145 | } 146 | } else { 147 | resps3 := parse(resp.Message) 148 | logrus.Info(newError("received XOR-MAPPED-ADDRESS: ", resps3.xorAddr)) 149 | if resps3.xorAddr.String() == resps.xorAddr.String() { 150 | logrus.Info(newError("NAT mapping behavior: address dependent")) 151 | return AddressDependent, nil 152 | } 153 | } 154 | 155 | logrus.Info(newError("NAT mapping behavior: address and port dependent")) 156 | return AddressAndPortDependent, nil 157 | } 158 | 159 | // RFC5780: 4.4. Determining NAT Filtering Behavior 160 | func filteringTests(mapTestConn *stunServerConn) (int, error) { 161 | defer mapTestConn.Close() 162 | // Test I: Regular binding request 163 | logrus.Info(newError("filtering test I: regular binding request")) 164 | request := stun.MustBuild(stun.TransactionID, stun.BindingRequest) 165 | 166 | resp, err := mapTestConn.roundTrip(request, mapTestConn.RemoteAddr) 167 | if err != nil { 168 | return NoResult, err 169 | } 170 | resps := parse(resp.Message) 171 | if resps.xorAddr == nil || resps.otherAddr == nil { 172 | err := newError("NAT discovery feature not supported by this server").Base(errNoOtherAddress) 173 | logrus.Warn(err) 174 | return NoResult, err 175 | } 176 | addr, err := net.ResolveUDPAddr("udp", resps.otherAddr.String()) 177 | if err != nil { 178 | err := newError("failed resolving OTHER-ADDRESS: ", resps.otherAddr).Base(err) 179 | logrus.Warn(err) 180 | return NoResult, err 181 | } 182 | mapTestConn.OtherAddr = addr 183 | 184 | // Test II: Request to change both IP and port 185 | logrus.Info(newError("filtering test II: request to change both IP and port")) 186 | request = stun.MustBuild(stun.TransactionID, stun.BindingRequest) 187 | request.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x06}) 188 | 189 | resp, err = mapTestConn.roundTrip(request, mapTestConn.RemoteAddr) 190 | if err == nil { 191 | parse(resp.Message) // just to print out the resp 192 | if resp.Addr.String() != mapTestConn.RemoteAddr.String() { 193 | logrus.Info(newError("NAT filtering behavior: endpoint independent")) 194 | return EndpointIndependent, nil 195 | } 196 | } else if !errors.Is(err, errTimedOut) { 197 | return NoResult, err // something else went wrong 198 | } 199 | 200 | // Test III: Request to change port only 201 | logrus.Info(newError("filtering test III: request to change port only")) 202 | request = stun.MustBuild(stun.TransactionID, stun.BindingRequest) 203 | request.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x02}) 204 | 205 | resp, err = mapTestConn.roundTrip(request, mapTestConn.RemoteAddr) 206 | if err == nil { 207 | parse(resp.Message) // just to print out the resp 208 | if resp.Addr.String() != mapTestConn.RemoteAddr.String() { 209 | logrus.Info(newError("NAT filtering behavior: address dependent")) 210 | return AddressDependent, nil 211 | } 212 | } else if !errors.Is(err, errTimedOut) { 213 | return NoResult, err 214 | } 215 | logrus.Info(newError("NAT filtering behavior: address and port dependent")) 216 | 217 | return AddressAndPortDependent, nil 218 | } 219 | 220 | // Parse a STUN message 221 | func parse(msg *stun.Message) (ret struct { 222 | xorAddr *stun.XORMappedAddress 223 | otherAddr *stun.OtherAddress 224 | respOrigin *stun.ResponseOrigin 225 | mappedAddr *stun.MappedAddress 226 | software *stun.Software 227 | }, 228 | ) { 229 | ret.mappedAddr = &stun.MappedAddress{} 230 | ret.xorAddr = &stun.XORMappedAddress{} 231 | ret.respOrigin = &stun.ResponseOrigin{} 232 | ret.otherAddr = &stun.OtherAddress{} 233 | ret.software = &stun.Software{} 234 | if ret.xorAddr.GetFrom(msg) != nil { 235 | ret.xorAddr = nil 236 | } 237 | if ret.otherAddr.GetFrom(msg) != nil { 238 | ret.otherAddr = nil 239 | } 240 | if ret.respOrigin.GetFrom(msg) != nil { 241 | ret.respOrigin = nil 242 | } 243 | if ret.mappedAddr.GetFrom(msg) != nil { 244 | ret.mappedAddr = nil 245 | } 246 | if ret.software.GetFrom(msg) != nil { 247 | ret.software = nil 248 | } 249 | logrus.Debug(newError(msg)) 250 | logrus.Debug(newError("MAPPED-ADDRESS: ", ret.mappedAddr)) 251 | logrus.Debug(newError("XOR-MAPPED-ADDRESS: ", ret.xorAddr)) 252 | logrus.Debug(newError("RESPONSE-ORIGIN: ", ret.respOrigin)) 253 | logrus.Debug(newError("OTHER-ADDRESS: ", ret.otherAddr)) 254 | logrus.Debug(newError("SOFTWARE: ", ret.software)) 255 | for _, attr := range msg.Attributes { 256 | switch attr.Type { 257 | case 258 | stun.AttrXORMappedAddress, 259 | stun.AttrOtherAddress, 260 | stun.AttrResponseOrigin, 261 | stun.AttrMappedAddress, 262 | stun.AttrSoftware: 263 | break //nolint: staticcheck 264 | default: 265 | logrus.Debug(newErrorf("%v (l=%v)", attr, attr.Length)) 266 | } 267 | } 268 | return ret 269 | } 270 | 271 | // Given an address string, returns a StunServerConn 272 | func connect(addrStr string, socksPort int) (*stunServerConn, error) { 273 | addr, err := net.ResolveUDPAddr("udp", addrStr) 274 | if err != nil { 275 | return nil, newError("failed to resolve server address ", addrStr).Base(err) 276 | } 277 | 278 | logrus.Info(newError("connecting to STUN server: ", addrStr)) 279 | 280 | var mapTestConn net.PacketConn 281 | 282 | socksConn, err := net.Dial("tcp", fmt.Sprint("127.0.0.1:", socksPort)) 283 | if err == nil { 284 | handshake, err := socks5.ClientHandshake(socksConn, socks5.ParseAddr(addrStr), socks5.CmdUDPAssociate, nil) 285 | if err != nil { 286 | logrus.Warn(newError("failed to do udp associate handshake").Base(err)) 287 | } 288 | udpConn, err := net.DialUDP("udp", nil, handshake.UDPAddr()) 289 | if err == nil { 290 | mapTestConn = &socksPacketConn{udpConn, socksConn} 291 | } 292 | } 293 | 294 | if mapTestConn == nil { 295 | mapTestConn, err = net.ListenUDP("udp", nil) 296 | if err != nil { 297 | return nil, newError("failed to listen udp").Base(err) 298 | } 299 | } 300 | 301 | logrus.Info(newError("local address: ", mapTestConn.LocalAddr())) 302 | logrus.Info(newError("remote address: ", addr)) 303 | 304 | mChan := listen(mapTestConn) 305 | 306 | return &stunServerConn{ 307 | conn: mapTestConn, 308 | LocalAddr: mapTestConn.LocalAddr(), 309 | RemoteAddr: addr, 310 | messageChan: mChan, 311 | }, nil 312 | } 313 | 314 | // Send request and wait for response or timeout 315 | func (c *stunServerConn) roundTrip(msg *stun.Message, addr net.Addr) (*stunResponse, error) { 316 | _ = msg.NewTransactionID() 317 | logrus.Debug(newErrorf("sending to %v: (%v bytes)", addr, msg.Length+messageHeaderSize)) 318 | logrus.Debug(newError(msg).AtDebug()) 319 | for _, attr := range msg.Attributes { 320 | logrus.Debug(newErrorf("%v (l=%v)", attr, attr.Length)) 321 | } 322 | _, err := c.conn.WriteTo(msg.Raw, addr) 323 | if err != nil { 324 | logrus.Warn(newError("error sending request to ", addr)) 325 | return nil, err 326 | } 327 | 328 | // Wait for response or timeout 329 | select { 330 | case r, ok := <-c.messageChan: 331 | if !ok { 332 | return nil, errResponseMessage 333 | } 334 | return r, nil 335 | case <-time.After(time.Duration(timeout) * time.Second): 336 | logrus.Info(newError("timed out waiting for response from server ", addr)) 337 | return nil, errTimedOut 338 | } 339 | } 340 | 341 | // taken from https://github.com/pion/stun/blob/master/cmd/stun-traversal/main.go 342 | func listen(conn net.PacketConn) (messages chan *stunResponse) { 343 | messages = make(chan *stunResponse) 344 | go func() { 345 | buffer := buf.New() 346 | defer buffer.Release() 347 | b := buffer.Extend(buf.Size) 348 | 349 | for { 350 | n, addr, err := conn.ReadFrom(b) 351 | if err != nil { 352 | close(messages) 353 | return 354 | } 355 | logrus.Info(newErrorf("response from %v: (%d bytes)", addr, n)) 356 | b = b[:n] 357 | 358 | r := &stunResponse{ 359 | Message: &stun.Message{ 360 | Raw: b, 361 | }, 362 | Addr: addr, 363 | } 364 | err = r.Message.Decode() 365 | if err != nil { 366 | logrus.Warn(newErrorf("error decoding message").Base(err)) 367 | close(messages) 368 | return 369 | } 370 | 371 | messages <- r 372 | } 373 | }() 374 | return 375 | } 376 | -------------------------------------------------------------------------------- /tun.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import "C" 4 | import ( 5 | "context" 6 | "fmt" 7 | "io" 8 | "math" 9 | "net" 10 | "os" 11 | "path/filepath" 12 | "sync" 13 | "sync/atomic" 14 | "time" 15 | 16 | "github.com/sirupsen/logrus" 17 | "github.com/v2fly/v2ray-core/v5" 18 | appOutbound "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" 19 | "github.com/v2fly/v2ray-core/v5/common/buf" 20 | v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" 21 | "github.com/v2fly/v2ray-core/v5/common/net/pingproto" 22 | "github.com/v2fly/v2ray-core/v5/common/session" 23 | "github.com/v2fly/v2ray-core/v5/features/dns/localdns" 24 | "github.com/v2fly/v2ray-core/v5/features/outbound" 25 | routing_session "github.com/v2fly/v2ray-core/v5/features/routing/session" 26 | "github.com/v2fly/v2ray-core/v5/proxy/wireguard" 27 | "github.com/v2fly/v2ray-core/v5/transport/internet" 28 | "golang.org/x/sys/unix" 29 | "libcore/comm" 30 | "libcore/gvisor" 31 | "libcore/nat" 32 | "libcore/tun" 33 | ) 34 | 35 | var _ tun.Handler = (*Tun2ray)(nil) 36 | 37 | type Tun2ray struct { 38 | dev tun.Tun 39 | router string 40 | v2ray *V2RayInstance 41 | sniffing bool 42 | overrideDestination bool 43 | debug bool 44 | 45 | dumpUid bool 46 | trafficStats bool 47 | pcap bool 48 | 49 | udpTable sync.Map 50 | appStats sync.Map 51 | lockTable sync.Map 52 | 53 | defaultOutboundForPing outbound.Handler 54 | } 55 | 56 | type TunConfig struct { 57 | FileDescriptor int32 58 | Protect bool 59 | Protector Protector 60 | MTU int32 61 | V2Ray *V2RayInstance 62 | Gateway4 string 63 | Gateway6 string 64 | BindUpstream Protector 65 | IPv6Mode int32 66 | Implementation int32 67 | Sniffing bool 68 | OverrideDestination bool 69 | Debug bool 70 | DumpUID bool 71 | TrafficStats bool 72 | PCap bool 73 | ErrorHandler ErrorHandler 74 | } 75 | 76 | type ErrorHandler interface { 77 | HandleError(err string) 78 | } 79 | 80 | func NewTun2ray(config *TunConfig) (*Tun2ray, error) { 81 | if config.Debug { 82 | logrus.SetLevel(logrus.DebugLevel) 83 | } else { 84 | logrus.SetLevel(logrus.WarnLevel) 85 | } 86 | t := &Tun2ray{ 87 | router: config.Gateway4, 88 | v2ray: config.V2Ray, 89 | sniffing: config.Sniffing, 90 | overrideDestination: config.OverrideDestination, 91 | debug: config.Debug, 92 | dumpUid: config.DumpUID, 93 | trafficStats: config.TrafficStats, 94 | } 95 | 96 | var err error 97 | switch config.Implementation { 98 | case comm.TunImplementationGVisor: 99 | var pcapFile *os.File 100 | if config.PCap { 101 | path := time.Now().UTC().String() 102 | path = externalAssetsPath + "/pcap/" + path + ".pcap" 103 | err = os.MkdirAll(filepath.Dir(path), 0o755) 104 | if err != nil { 105 | return nil, newError("unable to create pcap dir").Base(err) 106 | } 107 | pcapFile, err = os.Create(path) 108 | if err != nil { 109 | return nil, newError("unable to create pcap file").Base(err) 110 | } 111 | } 112 | 113 | t.dev, err = gvisor.New(config.FileDescriptor, config.MTU, t, gvisor.DefaultNIC, config.PCap, pcapFile, math.MaxUint32, config.IPv6Mode) 114 | case comm.TunImplementationSystem: 115 | t.dev, err = nat.New(config.FileDescriptor, config.MTU, t, config.IPv6Mode, config.ErrorHandler.HandleError) 116 | } 117 | 118 | if err != nil { 119 | return nil, err 120 | } 121 | 122 | if !config.Protect { 123 | config.Protector = noopProtectorInstance 124 | } 125 | 126 | dc := config.V2Ray.dnsClient 127 | internet.UseAlternativeSystemDialer(&protectedDialer{ 128 | protector: config.Protector, 129 | resolver: func(ctx context.Context, domain string) ([]net.IP, error) { 130 | ips, _, err := dc.LookupDefault(ctx, domain) 131 | return ips, err 132 | }, 133 | }) 134 | if config.BindUpstream != nil { 135 | pingproto.ControlFunc = func(fd uintptr) { 136 | config.BindUpstream.Protect(int32(fd)) 137 | } 138 | } else { 139 | pingproto.ControlFunc = func(fd uintptr) { 140 | config.Protector.Protect(int32(fd)) 141 | bindToUpstream(fd) 142 | } 143 | } 144 | if defaultOutbound, ok := t.v2ray.outboundManager.GetDefaultHandler().(*appOutbound.Handler); ok { 145 | if _, isWireGuard := defaultOutbound.GetOutbound().(*wireguard.Client); isWireGuard { 146 | t.defaultOutboundForPing = defaultOutbound 147 | } 148 | } 149 | 150 | internet.UseAlternativeSystemDNSDialer(&protectedDialer{ 151 | protector: config.Protector, 152 | resolver: func(ctx context.Context, domain string) ([]net.IP, error) { 153 | ips, _, err := localdns.Client().LookupDefault(ctx, domain) 154 | return ips, err 155 | }, 156 | }) 157 | 158 | return t, nil 159 | } 160 | 161 | func (t *Tun2ray) Close() { 162 | pingproto.ControlFunc = nil 163 | internet.UseAlternativeSystemDialer(nil) 164 | internet.UseAlternativeSystemDNSDialer(nil) 165 | comm.CloseIgnore(t.dev) 166 | } 167 | 168 | func (t *Tun2ray) NewConnection(source v2rayNet.Destination, destination v2rayNet.Destination, conn net.Conn) { 169 | element := v2rayNet.AddConnection(conn) 170 | defer v2rayNet.RemoveConnection(element) 171 | 172 | inbound := &session.Inbound{ 173 | Source: source, 174 | Tag: "tun", 175 | NetworkType: networkType, 176 | WifiSSID: wifiSSID, 177 | } 178 | 179 | isDns := destination.Address.String() == t.router 180 | if isDns { 181 | inbound.Tag = "dns-in" 182 | } 183 | 184 | var uid uint16 185 | var self bool 186 | 187 | if t.dumpUid || t.trafficStats { 188 | u, err := dumpUid(source, destination) 189 | if err == nil { 190 | uid = uint16(u) 191 | var info *UidInfo 192 | self = uid > 0 && int(uid) == os.Getuid() 193 | if t.debug && !self && uid >= 10000 { 194 | if err == nil { 195 | info, _ = uidDumper.GetUidInfo(int32(uid)) 196 | } 197 | if info == nil { 198 | logrus.Infof("[TCP] %s ==> %s", source.NetAddr(), destination.NetAddr()) 199 | } else { 200 | logrus.Infof("[TCP][%s (%d/%s)] %s ==> %s", info.Label, uid, info.PackageName, source.NetAddr(), destination.NetAddr()) 201 | } 202 | } 203 | 204 | if uid < 10000 { 205 | uid = 1000 206 | } 207 | 208 | inbound.Uid = uint32(uid) 209 | } 210 | } 211 | 212 | ctx := core.WithContext(context.Background(), t.v2ray.core) 213 | ctx = session.ContextWithInbound(ctx, inbound) 214 | 215 | if !isDns && t.sniffing { 216 | req := session.SniffingRequest{ 217 | Enabled: true, 218 | RouteOnly: !t.overrideDestination, 219 | } 220 | if t.sniffing { 221 | req.OverrideDestinationForProtocol = append(req.OverrideDestinationForProtocol, "http", "tls") 222 | } 223 | ctx = session.ContextWithContent(ctx, &session.Content{ 224 | SniffingRequest: req, 225 | }) 226 | } 227 | 228 | var stats *appStats 229 | if t.trafficStats && !self && !isDns { 230 | if iStats, exists := t.appStats.Load(uid); exists { 231 | stats = iStats.(*appStats) 232 | } else { 233 | iCond, loaded := t.lockTable.LoadOrStore(uid, sync.NewCond(&sync.Mutex{})) 234 | cond := iCond.(*sync.Cond) 235 | if loaded { 236 | cond.L.Lock() 237 | cond.Wait() 238 | iStats, exists = t.appStats.Load(uid) 239 | if !exists { 240 | panic("unexpected sync read failed") 241 | } 242 | stats = iStats.(*appStats) 243 | cond.L.Unlock() 244 | } else { 245 | stats = &appStats{} 246 | t.appStats.Store(uid, stats) 247 | t.lockTable.Delete(uid) 248 | cond.Broadcast() 249 | } 250 | } 251 | atomic.AddInt32(&stats.tcpConn, 1) 252 | atomic.AddUint32(&stats.tcpConnTotal, 1) 253 | atomic.StoreInt64(&stats.deactivateAt, 0) 254 | conn = NewStatsCounterConn(conn, &stats.uplink, &stats.downlink) 255 | stats.Lock() 256 | statsElement := stats.connections.PushBack(conn) 257 | stats.Unlock() 258 | defer func() { 259 | if atomic.AddInt32(&stats.tcpConn, -1)+atomic.LoadInt32(&stats.udpConn) == 0 { 260 | atomic.StoreInt64(&stats.deactivateAt, time.Now().Unix()) 261 | } 262 | stats.Lock() 263 | stats.connections.Remove(statsElement) 264 | stats.Unlock() 265 | }() 266 | } 267 | inbound.Conn = conn 268 | 269 | _ = t.v2ray.dispatcher.DispatchConn(ctx, destination, conn, true) 270 | } 271 | 272 | func (t *Tun2ray) NewPacket(source v2rayNet.Destination, destination v2rayNet.Destination, data *buf.Buffer, writeBack func([]byte, *net.UDPAddr) (int, error), closer io.Closer) { 273 | natKey := source.NetAddr() 274 | 275 | sendTo := func() bool { 276 | iConn, ok := t.udpTable.Load(natKey) 277 | if !ok { 278 | return false 279 | } 280 | conn := iConn.(packetConn) 281 | err := conn.writeTo(data, &net.UDPAddr{ 282 | IP: destination.Address.IP(), 283 | Port: int(destination.Port), 284 | }) 285 | if err != nil { 286 | _ = conn.Close() 287 | } 288 | return true 289 | } 290 | 291 | var cond *sync.Cond 292 | 293 | if sendTo() { 294 | comm.CloseIgnore(closer) 295 | return 296 | } else { 297 | iCond, loaded := t.lockTable.LoadOrStore(natKey, sync.NewCond(&sync.Mutex{})) 298 | cond = iCond.(*sync.Cond) 299 | if loaded { 300 | cond.L.Lock() 301 | cond.Wait() 302 | sendTo() 303 | cond.L.Unlock() 304 | 305 | comm.CloseIgnore(closer) 306 | return 307 | } 308 | } 309 | 310 | inbound := &session.Inbound{ 311 | Source: source, 312 | Tag: "tun", 313 | NetworkType: networkType, 314 | WifiSSID: wifiSSID, 315 | } 316 | isDns := destination.Address.String() == t.router 317 | 318 | if isDns { 319 | inbound.Tag = "dns-in" 320 | } 321 | 322 | var uid uint16 323 | var self bool 324 | 325 | if t.dumpUid || t.trafficStats { 326 | 327 | u, err := dumpUid(source, destination) 328 | if err == nil { 329 | if u > 19999 { 330 | logrus.Debug("bad connection owner ", u, ", reset to android.") 331 | u = 1000 332 | } 333 | 334 | uid = uint16(u) 335 | var info *UidInfo 336 | self = uid > 0 && int(uid) == os.Getuid() 337 | 338 | if t.debug && !self && uid >= 1000 { 339 | if err == nil { 340 | info, err = uidDumper.GetUidInfo(int32(uid)) 341 | if err != nil { 342 | uid = 1000 343 | info, err = uidDumper.GetUidInfo(int32(uid)) 344 | } 345 | } 346 | var tag string 347 | if !isDns { 348 | tag = "UDP" 349 | } else { 350 | tag = "DNS" 351 | } 352 | 353 | if info == nil { 354 | logrus.Infof("[%s] %s ==> %s", tag, source.NetAddr(), destination.NetAddr()) 355 | } else { 356 | logrus.Infof("[%s][%s (%d/%s)] %s ==> %s", tag, info.Label, uid, info.PackageName, source.NetAddr(), destination.NetAddr()) 357 | } 358 | } 359 | 360 | if uid < 10000 { 361 | uid = 1000 362 | } 363 | 364 | inbound.Uid = uint32(uid) 365 | } 366 | 367 | } 368 | 369 | ctx := core.WithContext(context.Background(), t.v2ray.core) 370 | ctx = session.ContextWithInbound(ctx, inbound) 371 | 372 | if !isDns && t.sniffing { 373 | req := session.SniffingRequest{ 374 | Enabled: true, 375 | RouteOnly: !t.overrideDestination, 376 | } 377 | if t.sniffing { 378 | req.OverrideDestinationForProtocol = append(req.OverrideDestinationForProtocol, "quic") 379 | } 380 | ctx = session.ContextWithContent(ctx, &session.Content{ 381 | SniffingRequest: req, 382 | }) 383 | } 384 | 385 | conn, err := t.v2ray.dialUDP(ctx, destination, time.Minute*5) 386 | if err != nil { 387 | logrus.Errorf("[UDP] dial failed: %s", err.Error()) 388 | return 389 | } 390 | element := v2rayNet.AddConnection(conn) 391 | defer v2rayNet.RemoveConnection(element) 392 | 393 | var stats *appStats 394 | if t.trafficStats && !self && !isDns { 395 | if iStats, exists := t.appStats.Load(uid); exists { 396 | stats = iStats.(*appStats) 397 | } else { 398 | iCond, loaded := t.lockTable.LoadOrStore(uid, sync.NewCond(&sync.Mutex{})) 399 | cond := iCond.(*sync.Cond) 400 | if loaded { 401 | cond.L.Lock() 402 | cond.Wait() 403 | iStats, exists = t.appStats.Load(uid) 404 | if !exists { 405 | panic("unexpected sync read failed") 406 | } 407 | stats = iStats.(*appStats) 408 | cond.L.Unlock() 409 | } else { 410 | stats = &appStats{} 411 | t.appStats.Store(uid, stats) 412 | t.lockTable.Delete(uid) 413 | cond.Broadcast() 414 | } 415 | } 416 | atomic.AddInt32(&stats.udpConn, 1) 417 | atomic.AddUint32(&stats.udpConnTotal, 1) 418 | atomic.StoreInt64(&stats.deactivateAt, 0) 419 | conn = statsPacketConn{conn, &stats.uplink, &stats.downlink} 420 | stats.Lock() 421 | statsElement := stats.connections.PushBack(conn) 422 | stats.Unlock() 423 | defer func() { 424 | if atomic.AddInt32(&stats.udpConn, -1)+atomic.LoadInt32(&stats.tcpConn) == 0 { 425 | atomic.StoreInt64(&stats.deactivateAt, time.Now().Unix()) 426 | } 427 | stats.Lock() 428 | stats.connections.Remove(statsElement) 429 | stats.Unlock() 430 | }() 431 | } 432 | 433 | t.udpTable.Store(natKey, conn) 434 | 435 | go sendTo() 436 | 437 | t.lockTable.Delete(natKey) 438 | cond.Broadcast() 439 | 440 | for { 441 | buffer, addr, err := conn.readFrom() 442 | if err != nil { 443 | break 444 | } 445 | if isDns { 446 | addr = nil 447 | } 448 | if addr, ok := addr.(*net.UDPAddr); ok { 449 | _, err = writeBack(buffer.Bytes(), addr) 450 | } else { 451 | _, err = writeBack(buffer.Bytes(), nil) 452 | } 453 | buffer.Release() 454 | if err != nil { 455 | break 456 | } 457 | } 458 | // close 459 | comm.CloseIgnore(closer) 460 | t.udpTable.Delete(natKey) 461 | } 462 | 463 | func (t *Tun2ray) NewPingPacket(source v2rayNet.Destination, destination v2rayNet.Destination, message *buf.Buffer, writeBack func([]byte) error, closer io.Closer) bool { 464 | natKey := fmt.Sprint(source.Address, "-", destination.Address) 465 | 466 | sendTo := func() bool { 467 | iConn, ok := t.udpTable.Load(natKey) 468 | if !ok { 469 | return false 470 | } 471 | conn := iConn.(packetConn) 472 | err := conn.writeTo(message, &net.UDPAddr{ 473 | IP: destination.Address.IP(), 474 | Port: int(destination.Port), 475 | }) 476 | if err != nil { 477 | _ = conn.Close() 478 | newError("failed to write ping request to ", destination.Address).Base(err).WriteToLog() 479 | } 480 | return true 481 | } 482 | 483 | var cond *sync.Cond 484 | 485 | if sendTo() { 486 | comm.CloseIgnore(closer) 487 | return true 488 | } else { 489 | iCond, loaded := t.lockTable.LoadOrStore(natKey, sync.NewCond(&sync.Mutex{})) 490 | cond = iCond.(*sync.Cond) 491 | if loaded { 492 | cond.L.Lock() 493 | cond.Wait() 494 | sendTo() 495 | cond.L.Unlock() 496 | 497 | comm.CloseIgnore(closer) 498 | return true 499 | } 500 | } 501 | 502 | defer func() { 503 | t.lockTable.Delete(natKey) 504 | cond.Broadcast() 505 | }() 506 | 507 | ctx := core.WithContext(context.Background(), t.v2ray.core) 508 | ctx = session.ContextWithInbound(ctx, &session.Inbound{ 509 | Source: source, 510 | Tag: "tun", 511 | NetworkType: networkType, 512 | WifiSSID: wifiSSID, 513 | }) 514 | ctx = session.ContextWithOutbound(ctx, &session.Outbound{Target: destination}) 515 | ctx = session.ContextWithContent(ctx, &session.Content{Protocol: "ping"}) 516 | 517 | var handler outbound.Handler 518 | if route, err := t.v2ray.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil { 519 | tag := route.GetOutboundTag() 520 | handler = t.v2ray.outboundManager.GetHandler(tag) 521 | if handler != nil { 522 | newError("taking detour [", tag, "] for [", destination.Address, "]").WriteToLog() 523 | } else { 524 | newError("non existing tag: ", tag).AtWarning().WriteToLog() 525 | return false 526 | } 527 | } else if t.defaultOutboundForPing != nil { 528 | handler = t.defaultOutboundForPing 529 | newError("default route for ", destination.Address).AtWarning().WriteToLog() 530 | 531 | } else { 532 | return false 533 | } 534 | 535 | conn := t.v2ray.handleUDP(ctx, handler, destination, time.Second*30) 536 | 537 | element := v2rayNet.AddConnection(conn) 538 | defer v2rayNet.RemoveConnection(element) 539 | 540 | t.udpTable.Store(natKey, conn) 541 | 542 | go sendTo() 543 | 544 | go func() { 545 | for { 546 | buffer, _, err := conn.readFrom() 547 | if err != nil { 548 | newError("failed to read ping response from ", destination.Address).Base(err).WriteToLog() 549 | break 550 | } 551 | err = writeBack(buffer.Bytes()) 552 | buffer.Release() 553 | if err != nil { 554 | if err != unix.ENETUNREACH { 555 | newError("failed to write ping response back").Base(err).WriteToLog() 556 | } 557 | break 558 | } 559 | } 560 | // close 561 | comm.CloseIgnore(closer) 562 | t.udpTable.Delete(natKey) 563 | }() 564 | 565 | return true 566 | } 567 | -------------------------------------------------------------------------------- /tun/tun.go: -------------------------------------------------------------------------------- 1 | package tun 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/buf" 7 | "github.com/v2fly/v2ray-core/v5/common/net" 8 | ) 9 | 10 | type Tun interface { 11 | io.Closer 12 | } 13 | 14 | type Handler interface { 15 | NewConnection(source net.Destination, destination net.Destination, conn net.Conn) 16 | NewPacket(source net.Destination, destination net.Destination, data *buf.Buffer, writeBack func([]byte, *net.UDPAddr) (int, error), closer io.Closer) 17 | NewPingPacket(source net.Destination, destination net.Destination, message *buf.Buffer, writeBack func([]byte) error, closer io.Closer) bool 18 | } 19 | -------------------------------------------------------------------------------- /uid.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/v2fly/v2ray-core/v5/common/net" 7 | ) 8 | 9 | var ( 10 | uidDumper UidDumper 11 | useProcfs bool 12 | ) 13 | 14 | type UidInfo struct { 15 | PackageName string 16 | Label string 17 | } 18 | 19 | type UidDumper interface { 20 | DumpUid(ipProto int32, srcIp string, srcPort int32, destIp string, destPort int32) (int32, error) 21 | GetUidInfo(uid int32) (*UidInfo, error) 22 | } 23 | 24 | func SetUidDumper(dumper UidDumper, procfs bool) { 25 | uidDumper = dumper 26 | useProcfs = procfs 27 | } 28 | 29 | func dumpUid(source net.Destination, destination net.Destination) (int32, error) { 30 | if useProcfs { 31 | return querySocketUidFromProcFs(source, destination), nil 32 | } else { 33 | var ipProto int32 34 | if destination.Network == net.Network_TCP { 35 | ipProto = syscall.IPPROTO_TCP 36 | } else { 37 | ipProto = syscall.IPPROTO_UDP 38 | } 39 | return uidDumper.DumpUid(ipProto, source.Address.IP().String(), int32(source.Port), destination.Address.IP().String(), int32(destination.Port)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /url.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "net/url" 7 | "strconv" 8 | "strings" 9 | _ "unsafe" 10 | ) 11 | 12 | type URL interface { 13 | GetScheme() string 14 | SetScheme(scheme string) 15 | GetOpaque() string 16 | SetOpaque(opaque string) 17 | GetUsername() string 18 | SetUsername(username string) 19 | GetPassword() string 20 | SetPassword(password string) error 21 | GetHost() string 22 | SetHost(host string) 23 | GetPort() int32 24 | SetPort(port int32) 25 | GetPath() string 26 | SetPath(path string) 27 | GetRawPath() string 28 | SetRawPath(rawPath string) error 29 | QueryParameterNotBlank(key string) string 30 | AddQueryParameter(key, value string) 31 | GetFragment() string 32 | SetRawFragment(rawFragment string) error 33 | GetString() string 34 | } 35 | 36 | var _ URL = (*netURL)(nil) 37 | 38 | type netURL struct { 39 | url.URL 40 | url.Values 41 | } 42 | 43 | func NewURL(scheme string) URL { 44 | u := new(netURL) 45 | u.Scheme = scheme 46 | u.Values = make(url.Values) 47 | return u 48 | } 49 | 50 | //go:linkname getScheme net/url.getScheme 51 | func getScheme(rawURL string) (scheme, path string, err error) 52 | 53 | //go:linkname parseAuthority net/url.parseAuthority 54 | func parseAuthority(authority string) (user *url.Userinfo, host string, err error) 55 | 56 | //go:linkname setFragment net/url.(*URL).setFragment 57 | func setFragment(u *url.URL, fragment string) error 58 | 59 | //go:linkname setPath net/url.(*URL).setPath 60 | func setPath(u *url.URL, fragment string) error 61 | 62 | // parse parses a URL from a string in one of two contexts. If 63 | // viaRequest is true, the URL is assumed to have arrived via an HTTP request, 64 | // in which case only absolute URLs or path-absolute relative URLs are allowed. 65 | // If viaRequest is false, all forms of relative URLs are allowed. 66 | func parse(rawURL string) (*url.URL, error) { 67 | var rest string 68 | var err error 69 | 70 | url := new(url.URL) 71 | 72 | if rawURL == "*" { 73 | url.Path = "*" 74 | return url, nil 75 | } 76 | 77 | // Split off possible leading "http:", "mailto:", etc. 78 | // Cannot contain escaped characters. 79 | if url.Scheme, rest, err = getScheme(rawURL); err != nil { 80 | return nil, err 81 | } 82 | url.Scheme = strings.ToLower(url.Scheme) 83 | 84 | if strings.HasSuffix(rest, "?") && strings.Count(rest, "?") == 1 { 85 | url.ForceQuery = true 86 | rest = rest[:len(rest)-1] 87 | } else { 88 | rest, url.RawQuery, _ = strings.Cut(rest, "?") 89 | } 90 | 91 | if !strings.HasPrefix(rest, "/") { 92 | if url.Scheme != "" { 93 | // We consider rootless paths per RFC 3986 as opaque. 94 | url.Opaque = rest 95 | return url, nil 96 | } 97 | 98 | // Avoid confusion with malformed schemes, like cache_object:foo/bar. 99 | // See golang.org/issue/16822. 100 | // 101 | // RFC 3986, §3.3: 102 | // In addition, a URI reference (Section 4.1) may be a relative-path reference, 103 | // in which case the first path segment cannot contain a colon (":") character. 104 | if segment, _, _ := strings.Cut(rest, "/"); strings.Contains(segment, ":") { 105 | // First path segment has colon. Not allowed in relative URL. 106 | return nil, errors.New("first path segment in URL cannot contain colon") 107 | } 108 | } 109 | 110 | if (url.Scheme != "" || !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") { 111 | var authority string 112 | authority, rest = rest[2:], "" 113 | if i := strings.Index(authority, "/"); i >= 0 { 114 | authority, rest = authority[:i], authority[i:] 115 | } 116 | url.User, url.Host, err = parseAuthority(authority) 117 | if err != nil { 118 | return nil, err 119 | } 120 | } 121 | // Set Path and, optionally, RawPath. 122 | // RawPath is a hint of the encoding of Path. We don't want to set it if 123 | // the default escaping of Path is equivalent, to help make sure that people 124 | // don't rely on it in general. 125 | if err := setPath(url, rest); err != nil { 126 | return nil, err 127 | } 128 | return url, nil 129 | } 130 | 131 | func ParseURL(rawURL string) (URL, error) { 132 | u := &netURL{} 133 | ru, frag, _ := strings.Cut(rawURL, "#") 134 | uu, err := parse(ru) 135 | if err != nil { 136 | return nil, newError("failed to parse url: ", rawURL).Base(err) 137 | } 138 | u.URL = *uu 139 | u.Values = u.Query() 140 | if u.Values == nil { 141 | u.Values = make(url.Values) 142 | } 143 | if frag == "" { 144 | return u, nil 145 | } 146 | if err = u.SetRawFragment(frag); err != nil { 147 | return nil, err 148 | } 149 | return u, nil 150 | } 151 | 152 | func (u *netURL) GetScheme() string { 153 | return u.Scheme 154 | } 155 | 156 | func (u *netURL) SetScheme(scheme string) { 157 | u.Scheme = scheme 158 | } 159 | 160 | func (u *netURL) GetOpaque() string { 161 | return u.Opaque 162 | } 163 | 164 | func (u *netURL) SetOpaque(opaque string) { 165 | u.Opaque = opaque 166 | } 167 | 168 | func (u *netURL) GetUsername() string { 169 | if u.User != nil { 170 | return u.User.Username() 171 | } 172 | return "" 173 | } 174 | 175 | func (u *netURL) SetUsername(username string) { 176 | if u.User != nil { 177 | if password, ok := u.User.Password(); !ok { 178 | u.User = url.User(username) 179 | } else { 180 | u.User = url.UserPassword(username, password) 181 | } 182 | } else { 183 | u.User = url.User(username) 184 | } 185 | } 186 | 187 | func (u *netURL) GetPassword() string { 188 | if u.User != nil { 189 | if password, ok := u.User.Password(); ok { 190 | return password 191 | } 192 | } 193 | return "" 194 | } 195 | 196 | func (u *netURL) SetPassword(password string) error { 197 | if u.User == nil { 198 | return newError("set username first") 199 | } 200 | u.User = url.UserPassword(u.User.Username(), password) 201 | return nil 202 | } 203 | 204 | func (u *netURL) GetHost() string { 205 | return u.Hostname() 206 | } 207 | 208 | func (u *netURL) SetHost(host string) { 209 | _, port, err := net.SplitHostPort(u.Host) 210 | if err == nil { 211 | u.Host = net.JoinHostPort(host, port) 212 | } else { 213 | u.Host = host 214 | } 215 | } 216 | 217 | func (u *netURL) GetPort() int32 { 218 | portStr := u.Port() 219 | if portStr == "" { 220 | return 0 221 | } 222 | port, _ := strconv.Atoi(portStr) 223 | return int32(port) 224 | } 225 | 226 | func (u *netURL) SetPort(port int32) { 227 | host, _, err := net.SplitHostPort(u.Host) 228 | if err == nil { 229 | u.Host = net.JoinHostPort(host, strconv.Itoa(int(port))) 230 | } else { 231 | u.Host = net.JoinHostPort(u.Host, strconv.Itoa(int(port))) 232 | } 233 | } 234 | 235 | func (u *netURL) GetPath() string { 236 | return u.Path 237 | } 238 | 239 | func (u *netURL) SetPath(path string) { 240 | u.Path = path 241 | u.RawPath = "" 242 | } 243 | 244 | func (u *netURL) GetRawPath() string { 245 | return u.RawPath 246 | } 247 | 248 | func (u *netURL) SetRawPath(rawPath string) error { 249 | return setPath(&u.URL, rawPath) 250 | } 251 | 252 | func (u *netURL) QueryParameterNotBlank(key string) string { 253 | return u.Get(key) 254 | } 255 | 256 | func (u *netURL) AddQueryParameter(key, value string) { 257 | u.Add(key, value) 258 | } 259 | 260 | func (u *netURL) GetFragment() string { 261 | return u.Fragment 262 | } 263 | 264 | func (u *netURL) SetRawFragment(rawFragment string) error { 265 | return setFragment(&u.URL, rawFragment) 266 | } 267 | 268 | func (u *netURL) GetString() string { 269 | u.RawQuery = u.Encode() 270 | return u.String() 271 | } 272 | -------------------------------------------------------------------------------- /urltest.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/rand" 7 | gonet "net" 8 | "net/http" 9 | "net/url" 10 | "time" 11 | 12 | "github.com/v2fly/v2ray-core/v5/app/proxyman" 13 | "github.com/v2fly/v2ray-core/v5/common/net" 14 | "github.com/v2fly/v2ray-core/v5/common/session" 15 | ) 16 | 17 | func UrlTest(instance *V2RayInstance, inbound string, link string, timeout int32) (int32, error) { 18 | connTestUrl, err := url.Parse(link) 19 | if err != nil { 20 | return 0, err 21 | } 22 | address := net.ParseAddress(connTestUrl.Hostname()) 23 | if address.Family().IsDomain() { 24 | resolver := &net.Resolver{ 25 | PreferGo: true, 26 | Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 27 | ctx = session.ContextWithContent(ctx, &session.Content{ 28 | Protocol: "dns", 29 | }) 30 | conn, err := instance.dialContext(ctx, net.Destination{ 31 | Network: net.Network_UDP, 32 | Address: dnsAddress, 33 | Port: 53, 34 | }) 35 | if err == nil { 36 | conn = &pinnedPacketConn{conn} 37 | } 38 | return conn, err 39 | }, 40 | } 41 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 42 | _, err = resolver.LookupIP(ctx, "ip", address.Domain()) 43 | cancel() 44 | if err != nil { 45 | return 0, err 46 | } 47 | } 48 | transport := &http.Transport{ 49 | TLSHandshakeTimeout: time.Duration(timeout) * time.Millisecond, 50 | DisableKeepAlives: true, 51 | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 52 | dest, err := net.ParseDestination(fmt.Sprintf("%s:%s", network, addr)) 53 | if err != nil { 54 | return nil, err 55 | } 56 | inConn, outConn := gonet.Pipe() 57 | if inbound != "" { 58 | ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: inbound, Conn: outConn}) 59 | } 60 | ctx = proxyman.SetPreferUseIP(ctx, true) 61 | go instance.dispatchContext(ctx, dest, outConn) 62 | return inConn, nil 63 | }, 64 | } 65 | req, err := http.NewRequestWithContext(context.Background(), "GET", link, nil) 66 | req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2)) 67 | if err != nil { 68 | return 0, err 69 | } 70 | start := time.Now() 71 | resp, err := (&http.Client{ 72 | Transport: transport, 73 | Timeout: time.Duration(timeout) * time.Millisecond, 74 | }).Do(req) 75 | if err == nil && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { 76 | err = fmt.Errorf("unexcpted response status: %d", resp.StatusCode) 77 | } 78 | if err != nil { 79 | return 0, err 80 | } 81 | return int32(time.Since(start).Milliseconds()), nil 82 | } 83 | -------------------------------------------------------------------------------- /v2ray.go: -------------------------------------------------------------------------------- 1 | package libcore 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | "sync" 11 | "time" 12 | 13 | "github.com/v2fly/v2ray-core/v5" 14 | "github.com/v2fly/v2ray-core/v5/common" 15 | "github.com/v2fly/v2ray-core/v5/common/buf" 16 | "github.com/v2fly/v2ray-core/v5/common/net" 17 | "github.com/v2fly/v2ray-core/v5/common/protocol/udp" 18 | commonSerial "github.com/v2fly/v2ray-core/v5/common/serial" 19 | "github.com/v2fly/v2ray-core/v5/common/signal" 20 | "github.com/v2fly/v2ray-core/v5/features" 21 | "github.com/v2fly/v2ray-core/v5/features/dns" 22 | "github.com/v2fly/v2ray-core/v5/features/extension" 23 | "github.com/v2fly/v2ray-core/v5/features/outbound" 24 | "github.com/v2fly/v2ray-core/v5/features/routing" 25 | "github.com/v2fly/v2ray-core/v5/features/stats" 26 | "github.com/v2fly/v2ray-core/v5/infra/conf/serial" 27 | _ "github.com/v2fly/v2ray-core/v5/main/distro/minimal" 28 | "github.com/v2fly/v2ray-core/v5/proxy/vmess" 29 | vmessOutbound "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" 30 | "github.com/v2fly/v2ray-core/v5/transport" 31 | "github.com/v2fly/v2ray-core/v5/transport/pipe" 32 | ) 33 | 34 | func GetV2RayVersion() string { 35 | return core.Version() 36 | } 37 | 38 | type V2RayInstance struct { 39 | started bool 40 | core *core.Instance 41 | dispatcher routing.Dispatcher 42 | router routing.Router 43 | outboundManager outbound.Manager 44 | statsManager stats.Manager 45 | observatory features.TaggedFeatures 46 | dnsClient dns.NewClient 47 | } 48 | 49 | func NewV2rayInstance() *V2RayInstance { 50 | return &V2RayInstance{} 51 | } 52 | 53 | func (instance *V2RayInstance) LoadConfig(content string) error { 54 | config, err := serial.LoadJSONConfig(strings.NewReader(content)) 55 | if err != nil { 56 | if strings.HasSuffix(err.Error(), "geoip.dat: no such file or directory") { 57 | err = extractAssetName(geoipDat, true) 58 | } else if strings.HasSuffix(err.Error(), "not found in geoip.dat") { 59 | err = extractAssetName(geoipDat, false) 60 | } else if strings.HasSuffix(err.Error(), "geosite.dat: no such file or directory") { 61 | err = extractAssetName(geositeDat, true) 62 | } else if strings.HasSuffix(err.Error(), "not found in geosite.dat") { 63 | err = extractAssetName(geositeDat, false) 64 | } 65 | if err == nil { 66 | config, err = serial.LoadJSONConfig(strings.NewReader(content)) 67 | } 68 | } 69 | if err != nil { 70 | return err 71 | } 72 | if config.Outbound != nil { 73 | for _, outbound := range config.Outbound { 74 | if outbound.ProxySettings == nil { 75 | continue 76 | } 77 | proxyConfig, err := commonSerial.GetInstanceOf(outbound.ProxySettings) 78 | if err != nil { 79 | continue 80 | } 81 | proxy, ok := proxyConfig.(*vmessOutbound.Config) 82 | if !ok { 83 | continue 84 | } 85 | var reset bool 86 | for _, endpoint := range proxy.Receiver { 87 | for _, user := range endpoint.User { 88 | if user.Account == nil { 89 | continue 90 | } 91 | accountConfig, err := commonSerial.GetInstanceOf(user.Account) 92 | if err != nil { 93 | continue 94 | } 95 | account, ok := accountConfig.(*vmess.Account) 96 | if !ok { 97 | continue 98 | } 99 | if account.AlterId > 0 { 100 | account.AlterId = 0 101 | user.Account = commonSerial.ToTypedMessage(account) 102 | reset = true 103 | } 104 | } 105 | } 106 | if reset { 107 | outbound.ProxySettings = commonSerial.ToTypedMessage(proxy) 108 | } 109 | } 110 | } 111 | 112 | c, err := core.New(config) 113 | if err != nil { 114 | return err 115 | } 116 | instance.core = c 117 | instance.statsManager = c.GetFeature(stats.ManagerType()).(stats.Manager) 118 | instance.router = c.GetFeature(routing.RouterType()).(routing.Router) 119 | instance.outboundManager = c.GetFeature(outbound.ManagerType()).(outbound.Manager) 120 | instance.dispatcher = c.GetFeature(routing.DispatcherType()).(routing.Dispatcher) 121 | instance.dnsClient = c.GetFeature(dns.ClientType()).(dns.NewClient) 122 | 123 | o := c.GetFeature(extension.ObservatoryType()) 124 | if o != nil { 125 | instance.observatory = o.(features.TaggedFeatures) 126 | } 127 | return nil 128 | } 129 | 130 | func (instance *V2RayInstance) Start(errorHandler ErrorHandler) error { 131 | if instance.started { 132 | return errors.New("already started") 133 | } 134 | if instance.core == nil { 135 | return errors.New("not initialized") 136 | } 137 | instance.core.SetErrorHandler(func(err error) { 138 | errorHandler.HandleError(err.Error()) 139 | }) 140 | err := instance.core.Start() 141 | if err != nil { 142 | return err 143 | } 144 | instance.started = true 145 | return nil 146 | } 147 | 148 | func (instance *V2RayInstance) QueryStats(tag string, direct string) int64 { 149 | if instance.statsManager == nil { 150 | return 0 151 | } 152 | counter := instance.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct)) 153 | if counter == nil { 154 | return 0 155 | } 156 | return counter.Set(0) 157 | } 158 | 159 | func (instance *V2RayInstance) Close() error { 160 | if instance.started { 161 | err := instance.core.Close() 162 | if err == nil { 163 | *instance = V2RayInstance{} 164 | } 165 | return err 166 | } 167 | return nil 168 | } 169 | 170 | func getLink(ctx context.Context) (*transport.Link, *transport.Link) { 171 | opt := pipe.OptionsFromContext(ctx) 172 | uplinkReader, uplinkWriter := pipe.New(opt...) 173 | downlinkReader, downlinkWriter := pipe.New(opt...) 174 | 175 | inboundLink := &transport.Link{ 176 | Reader: downlinkReader, 177 | Writer: uplinkWriter, 178 | } 179 | 180 | outboundLink := &transport.Link{ 181 | Reader: uplinkReader, 182 | Writer: downlinkWriter, 183 | } 184 | return inboundLink, outboundLink 185 | } 186 | 187 | func (instance *V2RayInstance) dialContext(ctx context.Context, destination net.Destination) (net.Conn, error) { 188 | if !instance.started { 189 | return nil, os.ErrInvalid 190 | } 191 | ctx = core.WithContext(ctx, instance.core) 192 | r, err := instance.dispatcher.Dispatch(ctx, destination) 193 | if err != nil { 194 | return nil, err 195 | } 196 | var readerOpt buf.ConnectionOption 197 | if destination.Network == net.Network_TCP { 198 | readerOpt = buf.ConnectionOutputMulti(r.Reader) 199 | } else { 200 | readerOpt = buf.ConnectionOutputMultiUDP(r.Reader) 201 | } 202 | return buf.NewConnection(buf.ConnectionInputMulti(r.Writer), readerOpt), nil 203 | } 204 | 205 | func (instance *V2RayInstance) dispatchContext(ctx context.Context, destination net.Destination, conn net.Conn) error { 206 | if !instance.started { 207 | return os.ErrInvalid 208 | } 209 | ctx = core.WithContext(ctx, instance.core) 210 | return instance.dispatcher.DispatchLink(ctx, destination, &transport.Link{ 211 | Reader: buf.NewReader(conn), 212 | Writer: buf.NewWriter(conn), 213 | }) 214 | } 215 | 216 | func (instance *V2RayInstance) dialUDP(ctx context.Context, destination net.Destination, timeout time.Duration) (packetConn, error) { 217 | if !instance.started { 218 | return nil, os.ErrInvalid 219 | } 220 | ctx, cancel := context.WithCancel(ctx) 221 | link, err := instance.dispatcher.Dispatch(ctx, destination) 222 | if err != nil { 223 | cancel() 224 | return nil, err 225 | } 226 | c := &dispatcherConn{ 227 | dest: destination, 228 | link: link, 229 | ctx: ctx, 230 | cancel: cancel, 231 | cache: make(chan *udp.Packet, 16), 232 | } 233 | c.timer = signal.CancelAfterInactivity(ctx, func() { 234 | c.Close() 235 | }, timeout) 236 | go c.handleInput() 237 | return c, nil 238 | } 239 | 240 | func (instance *V2RayInstance) handleUDP(ctx context.Context, handler outbound.Handler, destination net.Destination, timeout time.Duration) packetConn { 241 | ctx, cancel := context.WithCancel(ctx) 242 | inboundLink, outboundLink := getLink(ctx) 243 | go handler.Dispatch(ctx, outboundLink) 244 | c := &dispatcherConn{ 245 | dest: destination, 246 | link: inboundLink, 247 | ctx: ctx, 248 | cancel: cancel, 249 | cache: make(chan *udp.Packet, 16), 250 | } 251 | c.timer = signal.CancelAfterInactivity(ctx, func() { 252 | c.Close() 253 | }, timeout) 254 | go c.handleInput() 255 | return c 256 | } 257 | 258 | var _ packetConn = (*dispatcherConn)(nil) 259 | 260 | type dispatcherConn struct { 261 | access sync.Mutex 262 | dest net.Destination 263 | link *transport.Link 264 | timer *signal.ActivityTimer 265 | 266 | ctx context.Context 267 | cancel context.CancelFunc 268 | closed bool 269 | cache chan *udp.Packet 270 | } 271 | 272 | func (c *dispatcherConn) IsPipe() bool { 273 | return true 274 | } 275 | 276 | func (c *dispatcherConn) handleInput() { 277 | defer c.Close() 278 | for { 279 | select { 280 | case <-c.ctx.Done(): 281 | return 282 | default: 283 | } 284 | 285 | mb, err := c.link.Reader.ReadMultiBuffer() 286 | if err != nil { 287 | buf.ReleaseMulti(mb) 288 | return 289 | } 290 | c.timer.Update() 291 | for _, buffer := range mb { 292 | if buffer.Len() <= 0 { 293 | continue 294 | } 295 | packet := udp.Packet{ 296 | Payload: buffer, 297 | } 298 | if buffer.Endpoint == nil { 299 | packet.Source = c.dest 300 | } else { 301 | packet.Source = *buffer.Endpoint 302 | } 303 | if packet.Source.Address.Family().IsDomain() { 304 | packet.Source.Address = net.AnyIP 305 | } 306 | select { 307 | case c.cache <- &packet: 308 | continue 309 | case <-c.ctx.Done(): 310 | default: 311 | } 312 | buffer.Release() 313 | } 314 | } 315 | } 316 | 317 | func (c *dispatcherConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 318 | select { 319 | case <-c.ctx.Done(): 320 | return 0, nil, io.EOF 321 | case packet := <-c.cache: 322 | n := copy(p, packet.Payload.Bytes()) 323 | packet.Payload.Release() 324 | return n, &net.UDPAddr{ 325 | IP: packet.Source.Address.IP(), 326 | Port: int(packet.Source.Port), 327 | }, nil 328 | } 329 | } 330 | 331 | func (c *dispatcherConn) readFrom() (buffer *buf.Buffer, addr net.Addr, err error) { 332 | select { 333 | case <-c.ctx.Done(): 334 | return nil, nil, io.EOF 335 | case packet, ok := <-c.cache: 336 | if !ok { 337 | return nil, nil, io.EOF 338 | } 339 | return packet.Payload, &net.UDPAddr{ 340 | IP: packet.Source.Address.IP(), 341 | Port: int(packet.Source.Port), 342 | }, nil 343 | } 344 | } 345 | 346 | func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { 347 | buffer := buf.New() 348 | buffer.Write(p) 349 | endpoint := net.DestinationFromAddr(addr) 350 | buffer.Endpoint = &endpoint 351 | err = c.link.Writer.WriteMultiBuffer(buf.MultiBuffer{buffer}) 352 | if err != nil { 353 | buffer.Release() 354 | c.Close() 355 | return 0, err 356 | } else { 357 | c.timer.Update() 358 | n = len(p) 359 | } 360 | return 361 | } 362 | 363 | func (c *dispatcherConn) writeTo(buffer *buf.Buffer, addr net.Addr) (err error) { 364 | endpoint := net.DestinationFromAddr(addr) 365 | buffer.Endpoint = &endpoint 366 | err = c.link.Writer.WriteMultiBuffer(buf.MultiBuffer{buffer}) 367 | if err != nil { 368 | buffer.Release() 369 | c.Close() 370 | } else { 371 | c.timer.Update() 372 | } 373 | return 374 | } 375 | 376 | func (c *dispatcherConn) RemoteAddr() net.Addr { 377 | return nil 378 | } 379 | 380 | func (c *dispatcherConn) LocalAddr() net.Addr { 381 | return &net.UDPAddr{ 382 | IP: []byte{0, 0, 0, 0}, 383 | Port: 0, 384 | } 385 | } 386 | 387 | func (c *dispatcherConn) Close() error { 388 | if c.closed { 389 | return nil 390 | } 391 | c.closed = true 392 | 393 | c.cancel() 394 | _ = common.Interrupt(c.link.Reader) 395 | _ = common.Interrupt(c.link.Writer) 396 | close(c.cache) 397 | 398 | return nil 399 | } 400 | 401 | func (c *dispatcherConn) SetDeadline(t time.Time) error { 402 | return nil 403 | } 404 | 405 | func (c *dispatcherConn) SetReadDeadline(t time.Time) error { 406 | return nil 407 | } 408 | 409 | func (c *dispatcherConn) SetWriteDeadline(t time.Time) error { 410 | return nil 411 | } 412 | --------------------------------------------------------------------------------