├── .github └── workflows │ └── go.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── adapters ├── inbound │ ├── http.go │ ├── https.go │ ├── socket.go │ └── util.go └── outbound │ ├── base.go │ ├── direct.go │ ├── fallback.go │ ├── http.go │ ├── loadbalance.go │ ├── reject.go │ ├── selector.go │ ├── shadowsocks.go │ ├── shadowsocksr.go │ ├── snell.go │ ├── socks5.go │ ├── urltest.go │ ├── util.go │ └── vmess.go ├── build4android └── build.sh ├── common ├── cache │ ├── cache.go │ ├── cache_test.go │ ├── lrucache.go │ └── lrucache_test.go ├── murmur3 │ ├── murmur.go │ └── murmur32.go ├── observable │ ├── iterable.go │ ├── observable.go │ ├── observable_test.go │ └── subscriber.go ├── picker │ ├── picker.go │ └── picker_test.go ├── pool │ └── pool.go ├── queue │ └── queue.go └── structure │ ├── structure.go │ └── structure_test.go ├── component ├── auth │ └── auth.go ├── domain-trie │ ├── node.go │ ├── tire.go │ └── trie_test.go ├── fakeip │ ├── pool.go │ └── pool_test.go ├── nat │ └── table.go ├── simple-obfs │ ├── http.go │ └── tls.go ├── snell │ ├── cipher.go │ └── snell.go ├── socks5 │ └── socks5.go ├── v2ray-plugin │ ├── mux.go │ └── websocket.go └── vmess │ ├── aead.go │ ├── chunk.go │ ├── conn.go │ ├── user.go │ ├── vmess.go │ └── websocket.go ├── config ├── config.go ├── initial.go └── utils.go ├── constant ├── adapters.go ├── metadata.go ├── path.go ├── rule.go ├── traffic.go └── version.go ├── dns ├── client.go ├── doh.go ├── filters.go ├── iputil.go ├── middleware.go ├── resolver.go ├── server.go └── util.go ├── docs └── logo.png ├── go.mod ├── go.sum ├── hub ├── executor │ └── executor.go ├── hub.go └── route │ ├── configs.go │ ├── ctxkeys.go │ ├── errors.go │ ├── proxies.go │ ├── rules.go │ ├── server.go │ └── sysproxy.go ├── log ├── level.go └── log.go ├── main.go ├── proxy ├── auth │ └── auth.go ├── http │ └── server.go ├── listener.go ├── redir │ ├── tcp.go │ ├── tcp_darwin.go │ ├── tcp_freebsd.go │ ├── tcp_linux.go │ ├── tcp_linux_386.go │ ├── tcp_linux_other.go │ └── tcp_windows.go └── socks │ ├── tcp.go │ ├── udp.go │ └── utils.go ├── rules ├── domain.go ├── domain_keyword.go ├── domain_suffix.go ├── final.go ├── geoip.go ├── ipcidr.go └── port.go └── tunnel ├── connection.go ├── mode.go ├── tunnel.go └── util.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push, pull_request] 3 | jobs: 4 | 5 | build: 6 | name: Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - name: Set up Go 1.13 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.13 14 | id: go 15 | 16 | - name: Check out code into the Go module directory 17 | uses: actions/checkout@v1 18 | 19 | - name: Get dependencies 20 | run: | 21 | go test ./... 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin/* 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # dep 16 | vendor 17 | 18 | # GoLand 19 | .idea/* 20 | 21 | # macOS file 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - '1.13' 5 | install: 6 | - "go mod download" 7 | env: 8 | global: 9 | - NAME=clash 10 | - BINDIR=bin 11 | - GO111MODULE=on 12 | script: 13 | - go test ./... 14 | before_deploy: make -j releases 15 | deploy: 16 | provider: releases 17 | prerelease: true 18 | skip_cleanup: true 19 | api_key: 20 | secure: dp1tc1h0er7aaAZ1hY0Xk/cUKwB0ifsAjg6e0M/Ad5NC87oucP6ESNFkDu0e9rUS1yB826/VnVGzNE/Z5zdjXVzPft+g5v5oRxzI4BKLhf07t9s+x8Z+3sApTxdsC5BvcN9x+5yRbpDLQ3biDPxSFu86j7m2pkEWw6XYNZO3/5y+RZXX7zu+d4MzTLUaA2kWl7KQAP0tEJNuw9ACDhpkw7LYbU/8q3E76prOTeme5/AT6Gxj7XhKUNP27lazhhqBSWM14ybPANqojNLEfMFHN/Eu2phYO07MuLTd4zuOIuw9y65kgvTFcHRlORjwUhnviXyA69obQejjgDI1WDOtU4PqpFaSLrxWtKI6k5VNWHARYggDm/wKl0WG7F0Kgio1KiGGhDg2yrbseXr/zBNaDhBtTFh6XJffqqwmgby1PXB6PWwfvWXooJMaQiFZczLWeMBl8v6XbSN6jtMTh/PQlKai6BcDd4LM8GQ7VHpSeff4qXEU4Vpnadjgs8VDPOHng6/HV+wDs8q2LrlMbnxLWxbCjOMUB6w7YnSrwH9owzKSoUs/531I4tTCRQIgipJtTK2b881/8osVjdMGS1mDXhBWO+OM0LCAdORJz+kN4PIkXXvKLt6jX74k6z4M3swFaqqtlTduN2Yy/ErsjguQO1VZfHmcpNssmJXI5QB9sxA= 21 | file: bin/* 22 | file_glob: true 23 | on: 24 | repo: zu1k/clashr 25 | branch: master 26 | tags: true 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as builder 2 | 3 | RUN apk add --no-cache make git && \ 4 | wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz -O /tmp/GeoLite2-Country.tar.gz && \ 5 | tar zxvf /tmp/GeoLite2-Country.tar.gz -C /tmp && \ 6 | mv /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /Country.mmdb 7 | WORKDIR /clash-src 8 | COPY . /clash-src 9 | RUN go mod download && \ 10 | make linux-amd64 && \ 11 | mv ./bin/clash-linux-amd64 /clash 12 | 13 | FROM alpine:latest 14 | 15 | RUN apk add --no-cache ca-certificates 16 | COPY --from=builder /Country.mmdb /root/.config/clash/ 17 | COPY --from=builder /clash / 18 | ENTRYPOINT ["/clash"] 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dreamacro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=clashR 2 | BINDIR=bin 3 | VERSION=$(shell git describe --tags || echo "unknown version") 4 | BUILDTIME=$(shell date -u) 5 | GOBUILD=CGO_ENABLED=0 go build -ldflags '-X "github.com/zu1k/clashr/constant.Version=$(VERSION)" \ 6 | -X "github.com/zu1k/clashr/constant.BuildTime=$(BUILDTIME)" \ 7 | -w -s' 8 | 9 | PLATFORM_LIST = \ 10 | darwin-amd64 \ 11 | linux-386 \ 12 | linux-amd64 \ 13 | linux-armv5 \ 14 | linux-armv6 \ 15 | linux-armv7 \ 16 | linux-armv8 \ 17 | linux-mips-softfloat \ 18 | linux-mips-hardfloat \ 19 | linux-mipsle-softfloat \ 20 | linux-mipsle-hardfloat \ 21 | linux-mips64 \ 22 | linux-mips64le \ 23 | freebsd-386 \ 24 | freebsd-amd64 25 | 26 | WINDOWS_ARCH_LIST = \ 27 | windows-386 \ 28 | windows-amd64 29 | 30 | all: linux-amd64 darwin-amd64 windows-amd64 # Most used 31 | 32 | darwin-amd64: 33 | GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 34 | 35 | linux-386: 36 | GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 37 | 38 | linux-amd64: 39 | GOARCH=amd64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 40 | 41 | linux-armv5: 42 | GOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 43 | 44 | linux-armv6: 45 | GOARCH=arm GOOS=linux GOARM=6 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 46 | 47 | linux-armv7: 48 | GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 49 | 50 | linux-armv8: 51 | GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 52 | 53 | linux-mips-softfloat: 54 | GOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 55 | 56 | linux-mips-hardfloat: 57 | GOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 58 | 59 | linux-mipsle-softfloat: 60 | GOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 61 | 62 | linux-mipsle-hardfloat: 63 | GOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 64 | 65 | linux-mips64: 66 | GOARCH=mips64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 67 | 68 | linux-mips64le: 69 | GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 70 | 71 | freebsd-386: 72 | GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 73 | 74 | freebsd-amd64: 75 | GOARCH=amd64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 76 | 77 | windows-386: 78 | GOARCH=386 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe 79 | 80 | windows-amd64: 81 | GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe 82 | 83 | gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) 84 | zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST)) 85 | 86 | $(gz_releases): %.gz : % 87 | chmod +x $(BINDIR)/$(NAME)-$(basename $@) 88 | gzip -f -S -$(VERSION).gz $(BINDIR)/$(NAME)-$(basename $@) 89 | 90 | $(zip_releases): %.zip : % 91 | zip -m -j $(BINDIR)/$(NAME)-$(basename $@)-$(VERSION).zip $(BINDIR)/$(NAME)-$(basename $@).exe 92 | 93 | all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) 94 | 95 | releases: $(gz_releases) $(zip_releases) 96 | clean: 97 | rm $(BINDIR)/* 98 | -------------------------------------------------------------------------------- /adapters/inbound/http.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "strings" 7 | 8 | C "github.com/zu1k/clashr/constant" 9 | ) 10 | 11 | // HTTPAdapter is a adapter for HTTP connection 12 | type HTTPAdapter struct { 13 | net.Conn 14 | metadata *C.Metadata 15 | R *http.Request 16 | } 17 | 18 | // Metadata return destination metadata 19 | func (h *HTTPAdapter) Metadata() *C.Metadata { 20 | return h.metadata 21 | } 22 | 23 | // NewHTTP is HTTPAdapter generator 24 | func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter { 25 | metadata := parseHTTPAddr(request) 26 | if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { 27 | metadata.SrcIP = ip 28 | metadata.SrcPort = port 29 | } 30 | return &HTTPAdapter{ 31 | metadata: metadata, 32 | R: request, 33 | Conn: conn, 34 | } 35 | } 36 | 37 | // RemoveHopByHopHeaders remove hop-by-hop header 38 | func RemoveHopByHopHeaders(header http.Header) { 39 | // Strip hop-by-hop header based on RFC: 40 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 41 | // https://www.mnot.net/blog/2011/07/11/what_proxies_must_do 42 | 43 | header.Del("Proxy-Connection") 44 | header.Del("Proxy-Authenticate") 45 | header.Del("Proxy-Authorization") 46 | header.Del("TE") 47 | header.Del("Trailers") 48 | header.Del("Transfer-Encoding") 49 | header.Del("Upgrade") 50 | 51 | connections := header.Get("Connection") 52 | header.Del("Connection") 53 | if len(connections) == 0 { 54 | return 55 | } 56 | for _, h := range strings.Split(connections, ",") { 57 | header.Del(strings.TrimSpace(h)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /adapters/inbound/https.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | ) 7 | 8 | // NewHTTPS is HTTPAdapter generator 9 | func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter { 10 | metadata := parseHTTPAddr(request) 11 | if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { 12 | metadata.SrcIP = ip 13 | metadata.SrcPort = port 14 | } 15 | return &SocketAdapter{ 16 | metadata: metadata, 17 | Conn: conn, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /adapters/inbound/socket.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/zu1k/clashr/component/socks5" 7 | C "github.com/zu1k/clashr/constant" 8 | ) 9 | 10 | // SocketAdapter is a adapter for socks and redir connection 11 | type SocketAdapter struct { 12 | net.Conn 13 | metadata *C.Metadata 14 | } 15 | 16 | // Metadata return destination metadata 17 | func (s *SocketAdapter) Metadata() *C.Metadata { 18 | return s.metadata 19 | } 20 | 21 | // NewSocket is SocketAdapter generator 22 | func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, netType C.NetWork) *SocketAdapter { 23 | metadata := parseSocksAddr(target) 24 | metadata.NetWork = netType 25 | metadata.Type = source 26 | if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { 27 | metadata.SrcIP = ip 28 | metadata.SrcPort = port 29 | } 30 | 31 | return &SocketAdapter{ 32 | Conn: conn, 33 | metadata: metadata, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /adapters/inbound/util.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "strconv" 7 | 8 | "github.com/zu1k/clashr/component/socks5" 9 | C "github.com/zu1k/clashr/constant" 10 | ) 11 | 12 | func parseSocksAddr(target socks5.Addr) *C.Metadata { 13 | metadata := &C.Metadata{ 14 | AddrType: int(target[0]), 15 | } 16 | 17 | switch target[0] { 18 | case socks5.AtypDomainName: 19 | metadata.Host = string(target[2 : 2+target[1]]) 20 | metadata.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1])) 21 | case socks5.AtypIPv4: 22 | ip := net.IP(target[1 : 1+net.IPv4len]) 23 | metadata.DstIP = &ip 24 | metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1])) 25 | case socks5.AtypIPv6: 26 | ip := net.IP(target[1 : 1+net.IPv6len]) 27 | metadata.DstIP = &ip 28 | metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) 29 | } 30 | 31 | return metadata 32 | } 33 | 34 | func parseHTTPAddr(request *http.Request) *C.Metadata { 35 | host := request.URL.Hostname() 36 | port := request.URL.Port() 37 | if port == "" { 38 | port = "80" 39 | } 40 | 41 | metadata := &C.Metadata{ 42 | NetWork: C.TCP, 43 | Type: C.HTTP, 44 | AddrType: C.AtypDomainName, 45 | Host: host, 46 | DstIP: nil, 47 | DstPort: port, 48 | } 49 | 50 | ip := net.ParseIP(host) 51 | if ip != nil { 52 | switch { 53 | case ip.To4() == nil: 54 | metadata.AddrType = C.AtypIPv6 55 | default: 56 | metadata.AddrType = C.AtypIPv4 57 | } 58 | metadata.DstIP = &ip 59 | } 60 | 61 | return metadata 62 | } 63 | 64 | func parseAddr(addr string) (*net.IP, string, error) { 65 | host, port, err := net.SplitHostPort(addr) 66 | if err != nil { 67 | return nil, "", err 68 | } 69 | 70 | ip := net.ParseIP(host) 71 | return &ip, port, nil 72 | } 73 | -------------------------------------------------------------------------------- /adapters/outbound/base.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | "net/http" 9 | "time" 10 | 11 | "github.com/zu1k/clashr/common/queue" 12 | C "github.com/zu1k/clashr/constant" 13 | ) 14 | 15 | var ( 16 | defaultURLTestTimeout = time.Second * 5 17 | ) 18 | 19 | type Base struct { 20 | name string 21 | tp C.AdapterType 22 | udp bool 23 | } 24 | 25 | func (b *Base) Name() string { 26 | return b.name 27 | } 28 | 29 | func (b *Base) Type() C.AdapterType { 30 | return b.tp 31 | } 32 | 33 | func (b *Base) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 34 | return nil, nil, errors.New("no support") 35 | } 36 | 37 | func (b *Base) SupportUDP() bool { 38 | return b.udp 39 | } 40 | 41 | func (b *Base) Destroy() {} 42 | 43 | func (b *Base) MarshalJSON() ([]byte, error) { 44 | return json.Marshal(map[string]string{ 45 | "type": b.Type().String(), 46 | }) 47 | } 48 | 49 | type conn struct { 50 | net.Conn 51 | chain C.Chain 52 | } 53 | 54 | func (c *conn) Chains() C.Chain { 55 | return c.chain 56 | } 57 | 58 | func (c *conn) AppendToChains(a C.ProxyAdapter) { 59 | c.chain = append(c.chain, a.Name()) 60 | } 61 | 62 | func newConn(c net.Conn, a C.ProxyAdapter) C.Conn { 63 | return &conn{c, []string{a.Name()}} 64 | } 65 | 66 | type packetConn struct { 67 | net.PacketConn 68 | chain C.Chain 69 | } 70 | 71 | func (c *packetConn) Chains() C.Chain { 72 | return c.chain 73 | } 74 | 75 | func (c *packetConn) AppendToChains(a C.ProxyAdapter) { 76 | c.chain = append(c.chain, a.Name()) 77 | } 78 | 79 | func newPacketConn(c net.PacketConn, a C.ProxyAdapter) C.PacketConn { 80 | return &packetConn{c, []string{a.Name()}} 81 | } 82 | 83 | type Proxy struct { 84 | C.ProxyAdapter 85 | history *queue.Queue 86 | alive bool 87 | } 88 | 89 | func (p *Proxy) Alive() bool { 90 | return p.alive 91 | } 92 | 93 | func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) { 94 | ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout) 95 | defer cancel() 96 | return p.DialContext(ctx, metadata) 97 | } 98 | 99 | func (p *Proxy) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 100 | conn, err := p.ProxyAdapter.DialContext(ctx, metadata) 101 | if err != nil { 102 | p.alive = false 103 | } 104 | return conn, err 105 | } 106 | 107 | func (p *Proxy) DelayHistory() []C.DelayHistory { 108 | queue := p.history.Copy() 109 | histories := []C.DelayHistory{} 110 | for _, item := range queue { 111 | histories = append(histories, item.(C.DelayHistory)) 112 | } 113 | return histories 114 | } 115 | 116 | // LastDelay return last history record. if proxy is not alive, return the max value of uint16. 117 | func (p *Proxy) LastDelay() (delay uint16) { 118 | var max uint16 = 0xffff 119 | if !p.alive { 120 | return max 121 | } 122 | 123 | last := p.history.Last() 124 | if last == nil { 125 | return max 126 | } 127 | history := last.(C.DelayHistory) 128 | if history.Delay == 0 { 129 | return max 130 | } 131 | return history.Delay 132 | } 133 | 134 | func (p *Proxy) MarshalJSON() ([]byte, error) { 135 | inner, err := p.ProxyAdapter.MarshalJSON() 136 | if err != nil { 137 | return inner, err 138 | } 139 | 140 | mapping := map[string]interface{}{} 141 | json.Unmarshal(inner, &mapping) 142 | mapping["history"] = p.DelayHistory() 143 | return json.Marshal(mapping) 144 | } 145 | 146 | // URLTest get the delay for the specified URL 147 | func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) { 148 | defer func() { 149 | p.alive = err == nil 150 | record := C.DelayHistory{Time: time.Now()} 151 | if err == nil { 152 | record.Delay = t 153 | } 154 | p.history.Put(record) 155 | if p.history.Len() > 10 { 156 | p.history.Pop() 157 | } 158 | }() 159 | 160 | addr, err := urlToMetadata(url) 161 | if err != nil { 162 | return 163 | } 164 | 165 | start := time.Now() 166 | instance, err := p.DialContext(ctx, &addr) 167 | if err != nil { 168 | return 169 | } 170 | defer instance.Close() 171 | 172 | req, err := http.NewRequest(http.MethodHead, url, nil) 173 | if err != nil { 174 | return 175 | } 176 | req = req.WithContext(ctx) 177 | 178 | transport := &http.Transport{ 179 | Dial: func(string, string) (net.Conn, error) { 180 | return instance, nil 181 | }, 182 | // from http.DefaultTransport 183 | MaxIdleConns: 100, 184 | IdleConnTimeout: 90 * time.Second, 185 | TLSHandshakeTimeout: 10 * time.Second, 186 | ExpectContinueTimeout: 1 * time.Second, 187 | } 188 | 189 | client := http.Client{Transport: transport} 190 | resp, err := client.Do(req) 191 | if err != nil { 192 | return 193 | } 194 | resp.Body.Close() 195 | t = uint16(time.Since(start) / time.Millisecond) 196 | return 197 | } 198 | 199 | func NewProxy(adapter C.ProxyAdapter) *Proxy { 200 | return &Proxy{adapter, queue.New(10), true} 201 | } 202 | 203 | // ProxyGroupOption contain the common options for all kind of ProxyGroup 204 | type ProxyGroupOption struct { 205 | Name string `proxy:"name"` 206 | Proxies []string `proxy:"proxies"` 207 | } 208 | -------------------------------------------------------------------------------- /adapters/outbound/direct.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | C "github.com/zu1k/clashr/constant" 8 | ) 9 | 10 | type Direct struct { 11 | *Base 12 | } 13 | 14 | func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 15 | address := net.JoinHostPort(metadata.Host, metadata.DstPort) 16 | if metadata.DstIP != nil { 17 | address = net.JoinHostPort(metadata.DstIP.String(), metadata.DstPort) 18 | } 19 | 20 | c, err := dialContext(ctx, "tcp", address) 21 | if err != nil { 22 | return nil, err 23 | } 24 | tcpKeepAlive(c) 25 | return newConn(c, d), nil 26 | } 27 | 28 | func (d *Direct) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 29 | pc, err := net.ListenPacket("udp", "") 30 | if err != nil { 31 | return nil, nil, err 32 | } 33 | 34 | addr, err := resolveUDPAddr("udp", metadata.RemoteAddress()) 35 | if err != nil { 36 | return nil, nil, err 37 | } 38 | return newPacketConn(pc, d), addr, nil 39 | } 40 | 41 | func NewDirect() *Direct { 42 | return &Direct{ 43 | Base: &Base{ 44 | name: "DIRECT", 45 | tp: C.Direct, 46 | udp: true, 47 | }, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /adapters/outbound/fallback.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | "sync" 9 | "time" 10 | 11 | C "github.com/zu1k/clashr/constant" 12 | ) 13 | 14 | type Fallback struct { 15 | *Base 16 | proxies []C.Proxy 17 | rawURL string 18 | interval time.Duration 19 | done chan struct{} 20 | } 21 | 22 | type FallbackOption struct { 23 | Name string `proxy:"name"` 24 | Proxies []string `proxy:"proxies"` 25 | URL string `proxy:"url"` 26 | Interval int `proxy:"interval"` 27 | } 28 | 29 | func (f *Fallback) Now() string { 30 | proxy := f.findAliveProxy() 31 | return proxy.Name() 32 | } 33 | 34 | func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 35 | proxy := f.findAliveProxy() 36 | c, err := proxy.DialContext(ctx, metadata) 37 | if err == nil { 38 | c.AppendToChains(f) 39 | } 40 | return c, err 41 | } 42 | 43 | func (f *Fallback) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 44 | proxy := f.findAliveProxy() 45 | pc, addr, err := proxy.DialUDP(metadata) 46 | if err == nil { 47 | pc.AppendToChains(f) 48 | } 49 | return pc, addr, err 50 | } 51 | 52 | func (f *Fallback) SupportUDP() bool { 53 | proxy := f.findAliveProxy() 54 | return proxy.SupportUDP() 55 | } 56 | 57 | func (f *Fallback) MarshalJSON() ([]byte, error) { 58 | var all []string 59 | for _, proxy := range f.proxies { 60 | all = append(all, proxy.Name()) 61 | } 62 | return json.Marshal(map[string]interface{}{ 63 | "type": f.Type().String(), 64 | "now": f.Now(), 65 | "all": all, 66 | }) 67 | } 68 | 69 | func (f *Fallback) Destroy() { 70 | f.done <- struct{}{} 71 | } 72 | 73 | func (f *Fallback) loop() { 74 | tick := time.NewTicker(f.interval) 75 | go f.validTest() 76 | Loop: 77 | for { 78 | select { 79 | case <-tick.C: 80 | go f.validTest() 81 | case <-f.done: 82 | break Loop 83 | } 84 | } 85 | } 86 | 87 | func (f *Fallback) findAliveProxy() C.Proxy { 88 | for _, proxy := range f.proxies { 89 | if proxy.Alive() { 90 | return proxy 91 | } 92 | } 93 | return f.proxies[0] 94 | } 95 | 96 | func (f *Fallback) validTest() { 97 | wg := sync.WaitGroup{} 98 | wg.Add(len(f.proxies)) 99 | 100 | for _, p := range f.proxies { 101 | go func(p C.Proxy) { 102 | p.URLTest(context.Background(), f.rawURL) 103 | wg.Done() 104 | }(p) 105 | } 106 | 107 | wg.Wait() 108 | } 109 | 110 | func NewFallback(option FallbackOption, proxies []C.Proxy) (*Fallback, error) { 111 | _, err := urlToMetadata(option.URL) 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | if len(proxies) < 1 { 117 | return nil, errors.New("The number of proxies cannot be 0") 118 | } 119 | 120 | interval := time.Duration(option.Interval) * time.Second 121 | 122 | Fallback := &Fallback{ 123 | Base: &Base{ 124 | name: option.Name, 125 | tp: C.Fallback, 126 | }, 127 | proxies: proxies, 128 | rawURL: option.URL, 129 | interval: interval, 130 | done: make(chan struct{}), 131 | } 132 | go Fallback.loop() 133 | return Fallback, nil 134 | } 135 | -------------------------------------------------------------------------------- /adapters/outbound/http.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "context" 7 | "crypto/tls" 8 | "encoding/base64" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net" 13 | "net/http" 14 | "strconv" 15 | 16 | C "github.com/zu1k/clashr/constant" 17 | ) 18 | 19 | type Http struct { 20 | *Base 21 | addr string 22 | user string 23 | pass string 24 | tls bool 25 | skipCertVerify bool 26 | tlsConfig *tls.Config 27 | } 28 | 29 | type HttpOption struct { 30 | Name string `proxy:"name"` 31 | Server string `proxy:"server"` 32 | Port int `proxy:"port"` 33 | UserName string `proxy:"username,omitempty"` 34 | Password string `proxy:"password,omitempty"` 35 | TLS bool `proxy:"tls,omitempty"` 36 | SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 37 | } 38 | 39 | func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 40 | c, err := dialContext(ctx, "tcp", h.addr) 41 | if err == nil && h.tls { 42 | cc := tls.Client(c, h.tlsConfig) 43 | err = cc.Handshake() 44 | c = cc 45 | } 46 | 47 | if err != nil { 48 | return nil, fmt.Errorf("%s connect error", h.addr) 49 | } 50 | tcpKeepAlive(c) 51 | if err := h.shakeHand(metadata, c); err != nil { 52 | return nil, err 53 | } 54 | 55 | return newConn(c, h), nil 56 | } 57 | 58 | func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { 59 | var buf bytes.Buffer 60 | var err error 61 | 62 | addr := metadata.RemoteAddress() 63 | buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n") 64 | buf.WriteString("Host: " + metadata.String() + "\r\n") 65 | buf.WriteString("Proxy-Connection: Keep-Alive\r\n") 66 | 67 | if h.user != "" && h.pass != "" { 68 | auth := h.user + ":" + h.pass 69 | buf.WriteString("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n") 70 | } 71 | // header ended 72 | buf.WriteString("\r\n") 73 | 74 | _, err = rw.Write(buf.Bytes()) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | var req http.Request 80 | resp, err := http.ReadResponse(bufio.NewReader(rw), &req) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | if resp.StatusCode == http.StatusOK { 86 | return nil 87 | } 88 | 89 | if resp.StatusCode == http.StatusProxyAuthRequired { 90 | return errors.New("HTTP need auth") 91 | } 92 | 93 | if resp.StatusCode == http.StatusMethodNotAllowed { 94 | return errors.New("CONNECT method not allowed by proxy") 95 | } 96 | 97 | if resp.StatusCode >= http.StatusInternalServerError { 98 | return errors.New(resp.Status) 99 | } 100 | return fmt.Errorf("can not connect remote err code: %d", resp.StatusCode) 101 | } 102 | 103 | func NewHttp(option HttpOption) *Http { 104 | var tlsConfig *tls.Config 105 | if option.TLS { 106 | tlsConfig = &tls.Config{ 107 | InsecureSkipVerify: option.SkipCertVerify, 108 | ClientSessionCache: getClientSessionCache(), 109 | ServerName: option.Server, 110 | } 111 | } 112 | 113 | return &Http{ 114 | Base: &Base{ 115 | name: option.Name, 116 | tp: C.Http, 117 | }, 118 | addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 119 | user: option.UserName, 120 | pass: option.Password, 121 | tls: option.TLS, 122 | skipCertVerify: option.SkipCertVerify, 123 | tlsConfig: tlsConfig, 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /adapters/outbound/loadbalance.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | "sync" 9 | "time" 10 | 11 | "github.com/zu1k/clashr/common/murmur3" 12 | C "github.com/zu1k/clashr/constant" 13 | 14 | "golang.org/x/net/publicsuffix" 15 | ) 16 | 17 | type LoadBalance struct { 18 | *Base 19 | proxies []C.Proxy 20 | maxRetry int 21 | rawURL string 22 | interval time.Duration 23 | done chan struct{} 24 | } 25 | 26 | func getKey(metadata *C.Metadata) string { 27 | if metadata.Host != "" { 28 | // ip host 29 | if ip := net.ParseIP(metadata.Host); ip != nil { 30 | return metadata.Host 31 | } 32 | 33 | if etld, err := publicsuffix.EffectiveTLDPlusOne(metadata.Host); err == nil { 34 | return etld 35 | } 36 | } 37 | 38 | if metadata.DstIP == nil { 39 | return "" 40 | } 41 | 42 | return metadata.DstIP.String() 43 | } 44 | 45 | func jumpHash(key uint64, buckets int32) int32 { 46 | var b, j int64 47 | 48 | for j < int64(buckets) { 49 | b = j 50 | key = key*2862933555777941757 + 1 51 | j = int64(float64(b+1) * (float64(int64(1)<<31) / float64((key>>33)+1))) 52 | } 53 | 54 | return int32(b) 55 | } 56 | 57 | func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata) (c C.Conn, err error) { 58 | defer func() { 59 | if err == nil { 60 | c.AppendToChains(lb) 61 | } 62 | }() 63 | 64 | key := uint64(murmur3.Sum32([]byte(getKey(metadata)))) 65 | buckets := int32(len(lb.proxies)) 66 | for i := 0; i < lb.maxRetry; i, key = i+1, key+1 { 67 | idx := jumpHash(key, buckets) 68 | proxy := lb.proxies[idx] 69 | if proxy.Alive() { 70 | c, err = proxy.DialContext(ctx, metadata) 71 | return 72 | } 73 | } 74 | c, err = lb.proxies[0].DialContext(ctx, metadata) 75 | return 76 | } 77 | 78 | func (lb *LoadBalance) DialUDP(metadata *C.Metadata) (pc C.PacketConn, addr net.Addr, err error) { 79 | defer func() { 80 | if err == nil { 81 | pc.AppendToChains(lb) 82 | } 83 | }() 84 | 85 | key := uint64(murmur3.Sum32([]byte(getKey(metadata)))) 86 | buckets := int32(len(lb.proxies)) 87 | for i := 0; i < lb.maxRetry; i, key = i+1, key+1 { 88 | idx := jumpHash(key, buckets) 89 | proxy := lb.proxies[idx] 90 | if proxy.Alive() { 91 | return proxy.DialUDP(metadata) 92 | } 93 | } 94 | 95 | return lb.proxies[0].DialUDP(metadata) 96 | } 97 | 98 | func (lb *LoadBalance) SupportUDP() bool { 99 | return true 100 | } 101 | 102 | func (lb *LoadBalance) Destroy() { 103 | lb.done <- struct{}{} 104 | } 105 | 106 | func (lb *LoadBalance) validTest() { 107 | wg := sync.WaitGroup{} 108 | wg.Add(len(lb.proxies)) 109 | 110 | for _, p := range lb.proxies { 111 | go func(p C.Proxy) { 112 | p.URLTest(context.Background(), lb.rawURL) 113 | wg.Done() 114 | }(p) 115 | } 116 | 117 | wg.Wait() 118 | } 119 | 120 | func (lb *LoadBalance) loop() { 121 | tick := time.NewTicker(lb.interval) 122 | go lb.validTest() 123 | Loop: 124 | for { 125 | select { 126 | case <-tick.C: 127 | go lb.validTest() 128 | case <-lb.done: 129 | break Loop 130 | } 131 | } 132 | } 133 | 134 | func (lb *LoadBalance) MarshalJSON() ([]byte, error) { 135 | var all []string 136 | for _, proxy := range lb.proxies { 137 | all = append(all, proxy.Name()) 138 | } 139 | return json.Marshal(map[string]interface{}{ 140 | "type": lb.Type().String(), 141 | "all": all, 142 | }) 143 | } 144 | 145 | type LoadBalanceOption struct { 146 | Name string `proxy:"name"` 147 | Proxies []string `proxy:"proxies"` 148 | URL string `proxy:"url"` 149 | Interval int `proxy:"interval"` 150 | } 151 | 152 | func NewLoadBalance(option LoadBalanceOption, proxies []C.Proxy) (*LoadBalance, error) { 153 | if len(proxies) == 0 { 154 | return nil, errors.New("Provide at least one proxy") 155 | } 156 | 157 | interval := time.Duration(option.Interval) * time.Second 158 | 159 | lb := &LoadBalance{ 160 | Base: &Base{ 161 | name: option.Name, 162 | tp: C.LoadBalance, 163 | }, 164 | proxies: proxies, 165 | maxRetry: 3, 166 | rawURL: option.URL, 167 | interval: interval, 168 | done: make(chan struct{}), 169 | } 170 | go lb.loop() 171 | return lb, nil 172 | } 173 | -------------------------------------------------------------------------------- /adapters/outbound/reject.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "net" 7 | "time" 8 | 9 | C "github.com/zu1k/clashr/constant" 10 | ) 11 | 12 | type Reject struct { 13 | *Base 14 | } 15 | 16 | func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 17 | return newConn(&NopConn{}, r), nil 18 | } 19 | 20 | func NewReject() *Reject { 21 | return &Reject{ 22 | Base: &Base{ 23 | name: "REJECT", 24 | tp: C.Reject, 25 | }, 26 | } 27 | } 28 | 29 | type NopConn struct{} 30 | 31 | func (rw *NopConn) Read(b []byte) (int, error) { 32 | return 0, io.EOF 33 | } 34 | 35 | func (rw *NopConn) Write(b []byte) (int, error) { 36 | return 0, io.EOF 37 | } 38 | 39 | // Close is fake function for net.Conn 40 | func (rw *NopConn) Close() error { return nil } 41 | 42 | // LocalAddr is fake function for net.Conn 43 | func (rw *NopConn) LocalAddr() net.Addr { return nil } 44 | 45 | // RemoteAddr is fake function for net.Conn 46 | func (rw *NopConn) RemoteAddr() net.Addr { return nil } 47 | 48 | // SetDeadline is fake function for net.Conn 49 | func (rw *NopConn) SetDeadline(time.Time) error { return nil } 50 | 51 | // SetReadDeadline is fake function for net.Conn 52 | func (rw *NopConn) SetReadDeadline(time.Time) error { return nil } 53 | 54 | // SetWriteDeadline is fake function for net.Conn 55 | func (rw *NopConn) SetWriteDeadline(time.Time) error { return nil } 56 | -------------------------------------------------------------------------------- /adapters/outbound/selector.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | 9 | C "github.com/zu1k/clashr/constant" 10 | ) 11 | 12 | type Selector struct { 13 | *Base 14 | selected C.Proxy 15 | proxies map[string]C.Proxy 16 | proxyList []string 17 | } 18 | 19 | type SelectorOption struct { 20 | Name string `proxy:"name"` 21 | Proxies []string `proxy:"proxies"` 22 | } 23 | 24 | func (s *Selector) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 25 | c, err := s.selected.DialContext(ctx, metadata) 26 | if err == nil { 27 | c.AppendToChains(s) 28 | } 29 | return c, err 30 | } 31 | 32 | func (s *Selector) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 33 | pc, addr, err := s.selected.DialUDP(metadata) 34 | if err == nil { 35 | pc.AppendToChains(s) 36 | } 37 | return pc, addr, err 38 | } 39 | 40 | func (s *Selector) SupportUDP() bool { 41 | return s.selected.SupportUDP() 42 | } 43 | 44 | func (s *Selector) MarshalJSON() ([]byte, error) { 45 | return json.Marshal(map[string]interface{}{ 46 | "type": s.Type().String(), 47 | "now": s.Now(), 48 | "all": s.proxyList, 49 | }) 50 | } 51 | 52 | func (s *Selector) Now() string { 53 | return s.selected.Name() 54 | } 55 | 56 | func (s *Selector) Set(name string) error { 57 | proxy, exist := s.proxies[name] 58 | if !exist { 59 | return errors.New("Proxy does not exist") 60 | } 61 | s.selected = proxy 62 | return nil 63 | } 64 | 65 | func NewSelector(name string, proxies []C.Proxy) (*Selector, error) { 66 | if len(proxies) == 0 { 67 | return nil, errors.New("Provide at least one proxy") 68 | } 69 | 70 | mapping := make(map[string]C.Proxy) 71 | proxyList := make([]string, len(proxies)) 72 | for idx, proxy := range proxies { 73 | mapping[proxy.Name()] = proxy 74 | proxyList[idx] = proxy.Name() 75 | } 76 | 77 | s := &Selector{ 78 | Base: &Base{ 79 | name: name, 80 | tp: C.Selector, 81 | }, 82 | proxies: mapping, 83 | selected: proxies[0], 84 | proxyList: proxyList, 85 | } 86 | return s, nil 87 | } 88 | -------------------------------------------------------------------------------- /adapters/outbound/shadowsocksr.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | C "github.com/zu1k/clashr/constant" 8 | "github.com/zu1k/gossr/obfs" 9 | "github.com/zu1k/gossr/protocol" 10 | "net" 11 | "strconv" 12 | "strings" 13 | 14 | "github.com/zu1k/gossr" 15 | "github.com/zu1k/gossr/ssr" 16 | ) 17 | 18 | type ShadowsocksR struct { 19 | *Base 20 | server string 21 | //ssrquery *url.URL 22 | ssrop ShadowsocksROption 23 | ObfsData interface{} 24 | ProtocolData interface{} 25 | } 26 | 27 | type ShadowsocksROption struct { 28 | Name string `proxy:"name"` 29 | Server string `proxy:"server"` 30 | Port int `proxy:"port"` 31 | Password string `proxy:"password"` 32 | Cipher string `proxy:"cipher"` 33 | Protocol string `proxy:"protocol"` 34 | ProtocolParam string `proxy:"protocolparam"` 35 | Obfs string `proxy:"obfs"` 36 | ObfsParam string `proxy:"obfsparam"` 37 | } 38 | 39 | func (ssrins *ShadowsocksR) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 40 | ssrop := ssrins.ssrop 41 | cipher, err := shadowsocksr.NewStreamCipher(ssrop.Cipher, ssrop.Password) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | conn, err := dialContext(ctx, "tcp", ssrins.server) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | dstcon := shadowsocksr.NewSSTCPConn(conn, cipher) 52 | if dstcon.Conn == nil || dstcon.RemoteAddr() == nil { 53 | return nil, errors.New("nil connection") 54 | } 55 | 56 | rs := strings.Split(dstcon.RemoteAddr().String(), ":") 57 | port, _ := strconv.Atoi(rs[1]) 58 | 59 | if strings.HasSuffix(ssrop.Obfs, "_compatible") { 60 | ssrop.Obfs = strings.ReplaceAll(ssrop.Obfs, "_compatible", "") 61 | } 62 | dstcon.IObfs, err = obfs.NewObfs(ssrop.Obfs) 63 | if err != nil { 64 | return nil, err 65 | } 66 | obfsServerInfo := &ssr.ServerInfoForObfs{ 67 | Host: rs[0], 68 | Port: uint16(port), 69 | TcpMss: 1460, 70 | Param: ssrop.ObfsParam, 71 | } 72 | dstcon.IObfs.SetServerInfo(obfsServerInfo) 73 | 74 | if strings.HasSuffix(ssrop.Protocol, "_compatible") { 75 | ssrop.Protocol = strings.ReplaceAll(ssrop.Protocol, "_compatible", "") 76 | } 77 | dstcon.IProtocol, err = protocol.NewProtocol(ssrop.Protocol) 78 | if err != nil { 79 | return nil, err 80 | } 81 | protocolServerInfo := &ssr.ServerInfoForObfs{ 82 | Host: rs[0], 83 | Port: uint16(port), 84 | TcpMss: 1460, 85 | Param: ssrop.ProtocolParam, 86 | } 87 | dstcon.IProtocol.SetServerInfo(protocolServerInfo) 88 | 89 | if ssrins.ObfsData == nil { 90 | ssrins.ObfsData = dstcon.IObfs.GetData() 91 | } 92 | dstcon.IObfs.SetData(ssrins.ObfsData) 93 | 94 | if ssrins.ProtocolData == nil { 95 | ssrins.ProtocolData = dstcon.IProtocol.GetData() 96 | } 97 | dstcon.IProtocol.SetData(ssrins.ProtocolData) 98 | 99 | if _, err := dstcon.Write(serializesSocksAddr(metadata)); err != nil { 100 | _ = dstcon.Close() 101 | return nil, err 102 | } 103 | return newConn(dstcon, ssrins), err 104 | 105 | } 106 | 107 | func NewShadowsocksR(ssrop ShadowsocksROption) (*ShadowsocksR, error) { 108 | server := net.JoinHostPort(ssrop.Server, strconv.Itoa(ssrop.Port)) 109 | return &ShadowsocksR{ 110 | Base: &Base{ 111 | name: ssrop.Name, 112 | tp: C.ShadowsocksR, 113 | udp: false, 114 | }, 115 | server: server, 116 | //ssrquery: u, 117 | ssrop: ssrop, 118 | }, nil 119 | } 120 | 121 | func (ssr *ShadowsocksR) MarshalJSON() ([]byte, error) { 122 | return json.Marshal(map[string]string{ 123 | "type": ssr.Type().String(), 124 | }) 125 | } 126 | 127 | func (ssr *ShadowsocksR) DialUDP(metadata *C.Metadata) (pac C.PacketConn, netaddr net.Addr, err error) { 128 | return nil, nil, nil 129 | } 130 | -------------------------------------------------------------------------------- /adapters/outbound/snell.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | 9 | "github.com/zu1k/clashr/common/structure" 10 | obfs "github.com/zu1k/clashr/component/simple-obfs" 11 | "github.com/zu1k/clashr/component/snell" 12 | C "github.com/zu1k/clashr/constant" 13 | ) 14 | 15 | type Snell struct { 16 | *Base 17 | server string 18 | psk []byte 19 | obfsOption *simpleObfsOption 20 | } 21 | 22 | type SnellOption struct { 23 | Name string `proxy:"name"` 24 | Server string `proxy:"server"` 25 | Port int `proxy:"port"` 26 | Psk string `proxy:"psk"` 27 | ObfsOpts map[string]interface{} `proxy:"obfs-opts,omitempty"` 28 | } 29 | 30 | func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 31 | c, err := dialContext(ctx, "tcp", s.server) 32 | if err != nil { 33 | return nil, fmt.Errorf("%s connect error: %s", s.server, err.Error()) 34 | } 35 | tcpKeepAlive(c) 36 | switch s.obfsOption.Mode { 37 | case "tls": 38 | c = obfs.NewTLSObfs(c, s.obfsOption.Host) 39 | case "http": 40 | _, port, _ := net.SplitHostPort(s.server) 41 | c = obfs.NewHTTPObfs(c, s.obfsOption.Host, port) 42 | } 43 | c = snell.StreamConn(c, s.psk) 44 | port, _ := strconv.Atoi(metadata.DstPort) 45 | err = snell.WriteHeader(c, metadata.String(), uint(port)) 46 | return newConn(c, s), err 47 | } 48 | 49 | func NewSnell(option SnellOption) (*Snell, error) { 50 | server := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) 51 | psk := []byte(option.Psk) 52 | 53 | decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) 54 | obfsOption := &simpleObfsOption{Host: "bing.com"} 55 | if err := decoder.Decode(option.ObfsOpts, obfsOption); err != nil { 56 | return nil, fmt.Errorf("snell %s initialize obfs error: %s", server, err.Error()) 57 | } 58 | 59 | if obfsOption.Mode != "tls" && obfsOption.Mode != "http" { 60 | return nil, fmt.Errorf("snell %s obfs mode error: %s", server, obfsOption.Mode) 61 | } 62 | 63 | return &Snell{ 64 | Base: &Base{ 65 | name: option.Name, 66 | tp: C.Snell, 67 | }, 68 | server: server, 69 | psk: psk, 70 | obfsOption: obfsOption, 71 | }, nil 72 | } 73 | -------------------------------------------------------------------------------- /adapters/outbound/socks5.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net" 10 | "strconv" 11 | 12 | "github.com/zu1k/clashr/component/socks5" 13 | C "github.com/zu1k/clashr/constant" 14 | ) 15 | 16 | type Socks5 struct { 17 | *Base 18 | addr string 19 | user string 20 | pass string 21 | tls bool 22 | skipCertVerify bool 23 | tlsConfig *tls.Config 24 | } 25 | 26 | type Socks5Option struct { 27 | Name string `proxy:"name"` 28 | Server string `proxy:"server"` 29 | Port int `proxy:"port"` 30 | UserName string `proxy:"username,omitempty"` 31 | Password string `proxy:"password,omitempty"` 32 | TLS bool `proxy:"tls,omitempty"` 33 | UDP bool `proxy:"udp,omitempty"` 34 | SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 35 | } 36 | 37 | func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 38 | c, err := dialContext(ctx, "tcp", ss.addr) 39 | 40 | if err == nil && ss.tls { 41 | cc := tls.Client(c, ss.tlsConfig) 42 | err = cc.Handshake() 43 | c = cc 44 | } 45 | 46 | if err != nil { 47 | return nil, fmt.Errorf("%s connect error", ss.addr) 48 | } 49 | tcpKeepAlive(c) 50 | var user *socks5.User 51 | if ss.user != "" { 52 | user = &socks5.User{ 53 | Username: ss.user, 54 | Password: ss.pass, 55 | } 56 | } 57 | if _, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil { 58 | return nil, err 59 | } 60 | return newConn(c, ss), nil 61 | } 62 | 63 | func (ss *Socks5) DialUDP(metadata *C.Metadata) (_ C.PacketConn, _ net.Addr, err error) { 64 | ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout) 65 | defer cancel() 66 | c, err := dialContext(ctx, "tcp", ss.addr) 67 | if err != nil { 68 | err = fmt.Errorf("%s connect error", ss.addr) 69 | return 70 | } 71 | 72 | if ss.tls { 73 | cc := tls.Client(c, ss.tlsConfig) 74 | err = cc.Handshake() 75 | c = cc 76 | } 77 | 78 | defer func() { 79 | if err != nil { 80 | c.Close() 81 | } 82 | }() 83 | 84 | tcpKeepAlive(c) 85 | var user *socks5.User 86 | if ss.user != "" { 87 | user = &socks5.User{ 88 | Username: ss.user, 89 | Password: ss.pass, 90 | } 91 | } 92 | 93 | bindAddr, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user) 94 | if err != nil { 95 | err = fmt.Errorf("%v client hanshake error", err) 96 | return 97 | } 98 | 99 | addr, err := net.ResolveUDPAddr("udp", bindAddr.String()) 100 | if err != nil { 101 | return 102 | } 103 | 104 | targetAddr := socks5.ParseAddr(metadata.RemoteAddress()) 105 | if targetAddr == nil { 106 | return nil, nil, fmt.Errorf("parse address error: %v:%v", metadata.String(), metadata.DstPort) 107 | } 108 | 109 | pc, err := net.ListenPacket("udp", "") 110 | if err != nil { 111 | return 112 | } 113 | 114 | go func() { 115 | io.Copy(ioutil.Discard, c) 116 | c.Close() 117 | // A UDP association terminates when the TCP connection that the UDP 118 | // ASSOCIATE request arrived on terminates. RFC1928 119 | pc.Close() 120 | }() 121 | 122 | return newPacketConn(&socksUDPConn{PacketConn: pc, rAddr: targetAddr, tcpConn: c}, ss), addr, nil 123 | } 124 | 125 | func NewSocks5(option Socks5Option) *Socks5 { 126 | var tlsConfig *tls.Config 127 | if option.TLS { 128 | tlsConfig = &tls.Config{ 129 | InsecureSkipVerify: option.SkipCertVerify, 130 | ClientSessionCache: getClientSessionCache(), 131 | ServerName: option.Server, 132 | } 133 | } 134 | 135 | return &Socks5{ 136 | Base: &Base{ 137 | name: option.Name, 138 | tp: C.Socks5, 139 | udp: option.UDP, 140 | }, 141 | addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 142 | user: option.UserName, 143 | pass: option.Password, 144 | tls: option.TLS, 145 | skipCertVerify: option.SkipCertVerify, 146 | tlsConfig: tlsConfig, 147 | } 148 | } 149 | 150 | type socksUDPConn struct { 151 | net.PacketConn 152 | rAddr socks5.Addr 153 | tcpConn net.Conn 154 | } 155 | 156 | func (uc *socksUDPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { 157 | packet, err := socks5.EncodeUDPPacket(uc.rAddr, b) 158 | if err != nil { 159 | return 160 | } 161 | return uc.PacketConn.WriteTo(packet, addr) 162 | } 163 | 164 | func (uc *socksUDPConn) ReadFrom(b []byte) (int, net.Addr, error) { 165 | n, a, e := uc.PacketConn.ReadFrom(b) 166 | if e != nil { 167 | return 0, nil, e 168 | } 169 | addr, payload, err := socks5.DecodeUDPPacket(b) 170 | if err != nil { 171 | return 0, nil, err 172 | } 173 | // due to DecodeUDPPacket is mutable, record addr length 174 | addrLength := len(addr) 175 | copy(b, payload) 176 | return n - addrLength - 3, a, nil 177 | } 178 | 179 | func (uc *socksUDPConn) Close() error { 180 | uc.tcpConn.Close() 181 | return uc.PacketConn.Close() 182 | } 183 | -------------------------------------------------------------------------------- /adapters/outbound/urltest.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/zu1k/clashr/common/picker" 12 | C "github.com/zu1k/clashr/constant" 13 | ) 14 | 15 | type URLTest struct { 16 | *Base 17 | proxies []C.Proxy 18 | rawURL string 19 | fast C.Proxy 20 | interval time.Duration 21 | done chan struct{} 22 | once int32 23 | } 24 | 25 | type URLTestOption struct { 26 | Name string `proxy:"name"` 27 | Proxies []string `proxy:"proxies"` 28 | URL string `proxy:"url"` 29 | Interval int `proxy:"interval"` 30 | } 31 | 32 | func (u *URLTest) Now() string { 33 | return u.fast.Name() 34 | } 35 | 36 | func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata) (c C.Conn, err error) { 37 | for i := 0; i < 3; i++ { 38 | c, err = u.fast.DialContext(ctx, metadata) 39 | if err == nil { 40 | c.AppendToChains(u) 41 | return 42 | } 43 | u.fallback() 44 | } 45 | return 46 | } 47 | 48 | func (u *URLTest) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 49 | pc, addr, err := u.fast.DialUDP(metadata) 50 | if err == nil { 51 | pc.AppendToChains(u) 52 | } 53 | return pc, addr, err 54 | } 55 | 56 | func (u *URLTest) SupportUDP() bool { 57 | return u.fast.SupportUDP() 58 | } 59 | 60 | func (u *URLTest) MarshalJSON() ([]byte, error) { 61 | var all []string 62 | for _, proxy := range u.proxies { 63 | all = append(all, proxy.Name()) 64 | } 65 | return json.Marshal(map[string]interface{}{ 66 | "type": u.Type().String(), 67 | "now": u.Now(), 68 | "all": all, 69 | }) 70 | } 71 | 72 | func (u *URLTest) Destroy() { 73 | u.done <- struct{}{} 74 | } 75 | 76 | func (u *URLTest) loop() { 77 | tick := time.NewTicker(u.interval) 78 | ctx, cancel := context.WithCancel(context.Background()) 79 | defer cancel() 80 | 81 | go u.speedTest(ctx) 82 | Loop: 83 | for { 84 | select { 85 | case <-tick.C: 86 | go u.speedTest(ctx) 87 | case <-u.done: 88 | break Loop 89 | } 90 | } 91 | } 92 | 93 | func (u *URLTest) fallback() { 94 | fast := u.proxies[0] 95 | min := fast.LastDelay() 96 | for _, proxy := range u.proxies[1:] { 97 | if !proxy.Alive() { 98 | continue 99 | } 100 | 101 | delay := proxy.LastDelay() 102 | if delay < min { 103 | fast = proxy 104 | min = delay 105 | } 106 | } 107 | u.fast = fast 108 | } 109 | 110 | func (u *URLTest) speedTest(ctx context.Context) { 111 | if !atomic.CompareAndSwapInt32(&u.once, 0, 1) { 112 | return 113 | } 114 | defer atomic.StoreInt32(&u.once, 0) 115 | 116 | ctx, cancel := context.WithTimeout(ctx, defaultURLTestTimeout) 117 | defer cancel() 118 | picker := picker.WithoutAutoCancel(ctx) 119 | for _, p := range u.proxies { 120 | proxy := p 121 | picker.Go(func() (interface{}, error) { 122 | _, err := proxy.URLTest(ctx, u.rawURL) 123 | if err != nil { 124 | return nil, err 125 | } 126 | return proxy, nil 127 | }) 128 | } 129 | 130 | fast := picker.WaitWithoutCancel() 131 | if fast != nil { 132 | u.fast = fast.(C.Proxy) 133 | } 134 | 135 | picker.Wait() 136 | } 137 | 138 | func NewURLTest(option URLTestOption, proxies []C.Proxy) (*URLTest, error) { 139 | _, err := urlToMetadata(option.URL) 140 | if err != nil { 141 | return nil, err 142 | } 143 | if len(proxies) < 1 { 144 | return nil, errors.New("The number of proxies cannot be 0") 145 | } 146 | 147 | interval := time.Duration(option.Interval) * time.Second 148 | urlTest := &URLTest{ 149 | Base: &Base{ 150 | name: option.Name, 151 | tp: C.URLTest, 152 | }, 153 | proxies: proxies[:], 154 | rawURL: option.URL, 155 | fast: proxies[0], 156 | interval: interval, 157 | done: make(chan struct{}), 158 | once: 0, 159 | } 160 | go urlTest.loop() 161 | return urlTest, nil 162 | } 163 | -------------------------------------------------------------------------------- /adapters/outbound/util.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/tls" 7 | "fmt" 8 | "net" 9 | "net/url" 10 | "strconv" 11 | "sync" 12 | "time" 13 | 14 | "github.com/zu1k/clashr/component/socks5" 15 | C "github.com/zu1k/clashr/constant" 16 | "github.com/zu1k/clashr/dns" 17 | ) 18 | 19 | const ( 20 | tcpTimeout = 5 * time.Second 21 | ) 22 | 23 | var ( 24 | globalClientSessionCache tls.ClientSessionCache 25 | once sync.Once 26 | ) 27 | 28 | func urlToMetadata(rawURL string) (addr C.Metadata, err error) { 29 | u, err := url.Parse(rawURL) 30 | if err != nil { 31 | return 32 | } 33 | 34 | port := u.Port() 35 | if port == "" { 36 | if u.Scheme == "https" { 37 | port = "443" 38 | } else if u.Scheme == "http" { 39 | port = "80" 40 | } else { 41 | err = fmt.Errorf("%s scheme not Support", rawURL) 42 | return 43 | } 44 | } 45 | 46 | addr = C.Metadata{ 47 | AddrType: C.AtypDomainName, 48 | Host: u.Hostname(), 49 | DstIP: nil, 50 | DstPort: port, 51 | } 52 | return 53 | } 54 | 55 | func tcpKeepAlive(c net.Conn) { 56 | if tcp, ok := c.(*net.TCPConn); ok { 57 | tcp.SetKeepAlive(true) 58 | tcp.SetKeepAlivePeriod(30 * time.Second) 59 | } 60 | } 61 | 62 | func getClientSessionCache() tls.ClientSessionCache { 63 | once.Do(func() { 64 | globalClientSessionCache = tls.NewLRUClientSessionCache(128) 65 | }) 66 | return globalClientSessionCache 67 | } 68 | 69 | func serializesSocksAddr(metadata *C.Metadata) []byte { 70 | var buf [][]byte 71 | aType := uint8(metadata.AddrType) 72 | p, _ := strconv.Atoi(metadata.DstPort) 73 | port := []byte{uint8(p >> 8), uint8(p & 0xff)} 74 | switch metadata.AddrType { 75 | case socks5.AtypDomainName: 76 | len := uint8(len(metadata.Host)) 77 | host := []byte(metadata.Host) 78 | buf = [][]byte{{aType, len}, host, port} 79 | case socks5.AtypIPv4: 80 | host := metadata.DstIP.To4() 81 | buf = [][]byte{{aType}, host, port} 82 | case socks5.AtypIPv6: 83 | host := metadata.DstIP.To16() 84 | buf = [][]byte{{aType}, host, port} 85 | } 86 | return bytes.Join(buf, nil) 87 | } 88 | 89 | func dialContext(ctx context.Context, network, address string) (net.Conn, error) { 90 | host, port, err := net.SplitHostPort(address) 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | dialer := net.Dialer{} 96 | 97 | returned := make(chan struct{}) 98 | defer close(returned) 99 | 100 | type dialResult struct { 101 | net.Conn 102 | error 103 | resolved bool 104 | ipv6 bool 105 | done bool 106 | } 107 | results := make(chan dialResult) 108 | var primary, fallback dialResult 109 | 110 | startRacer := func(ctx context.Context, host string, ipv6 bool) { 111 | result := dialResult{ipv6: ipv6, done: true} 112 | defer func() { 113 | select { 114 | case results <- result: 115 | case <-returned: 116 | if result.Conn != nil { 117 | result.Conn.Close() 118 | } 119 | } 120 | }() 121 | 122 | var ip net.IP 123 | if ipv6 { 124 | ip, result.error = dns.ResolveIPv6(host) 125 | } else { 126 | ip, result.error = dns.ResolveIPv4(host) 127 | } 128 | if result.error != nil { 129 | return 130 | } 131 | result.resolved = true 132 | 133 | if ipv6 { 134 | result.Conn, result.error = dialer.DialContext(ctx, "tcp6", net.JoinHostPort(ip.String(), port)) 135 | } else { 136 | result.Conn, result.error = dialer.DialContext(ctx, "tcp4", net.JoinHostPort(ip.String(), port)) 137 | } 138 | } 139 | 140 | go startRacer(ctx, host, false) 141 | go startRacer(ctx, host, true) 142 | 143 | for { 144 | select { 145 | case res := <-results: 146 | if res.error == nil { 147 | return res.Conn, nil 148 | } 149 | 150 | if !res.ipv6 { 151 | primary = res 152 | } else { 153 | fallback = res 154 | } 155 | 156 | if primary.done && fallback.done { 157 | if primary.resolved { 158 | return nil, primary.error 159 | } else if fallback.resolved { 160 | return nil, fallback.error 161 | } else { 162 | return nil, primary.error 163 | } 164 | } 165 | } 166 | } 167 | } 168 | 169 | func resolveUDPAddr(network, address string) (*net.UDPAddr, error) { 170 | host, port, err := net.SplitHostPort(address) 171 | if err != nil { 172 | return nil, err 173 | } 174 | 175 | ip, err := dns.ResolveIP(host) 176 | if err != nil { 177 | return nil, err 178 | } 179 | return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port)) 180 | } 181 | -------------------------------------------------------------------------------- /adapters/outbound/vmess.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/zu1k/clashr/component/vmess" 11 | C "github.com/zu1k/clashr/constant" 12 | ) 13 | 14 | type Vmess struct { 15 | *Base 16 | server string 17 | client *vmess.Client 18 | } 19 | 20 | type VmessOption struct { 21 | Name string `proxy:"name"` 22 | Server string `proxy:"server"` 23 | Port int `proxy:"port"` 24 | UUID string `proxy:"uuid"` 25 | AlterID int `proxy:"alterId"` 26 | Cipher string `proxy:"cipher"` 27 | TLS bool `proxy:"tls,omitempty"` 28 | UDP bool `proxy:"udp,omitempty"` 29 | Network string `proxy:"network,omitempty"` 30 | WSPath string `proxy:"ws-path,omitempty"` 31 | WSHeaders map[string]string `proxy:"ws-headers,omitempty"` 32 | SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 33 | } 34 | 35 | func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { 36 | c, err := dialContext(ctx, "tcp", v.server) 37 | if err != nil { 38 | return nil, fmt.Errorf("%s connect error", v.server) 39 | } 40 | tcpKeepAlive(c) 41 | c, err = v.client.New(c, parseVmessAddr(metadata)) 42 | return newConn(c, v), err 43 | } 44 | 45 | func (v *Vmess) DialUDP(metadata *C.Metadata) (C.PacketConn, net.Addr, error) { 46 | ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout) 47 | defer cancel() 48 | c, err := dialContext(ctx, "tcp", v.server) 49 | if err != nil { 50 | return nil, nil, fmt.Errorf("%s connect error", v.server) 51 | } 52 | tcpKeepAlive(c) 53 | c, err = v.client.New(c, parseVmessAddr(metadata)) 54 | if err != nil { 55 | return nil, nil, fmt.Errorf("new vmess client error: %v", err) 56 | } 57 | return newPacketConn(&vmessUDPConn{Conn: c}, v), c.RemoteAddr(), nil 58 | } 59 | 60 | func NewVmess(option VmessOption) (*Vmess, error) { 61 | security := strings.ToLower(option.Cipher) 62 | client, err := vmess.NewClient(vmess.Config{ 63 | UUID: option.UUID, 64 | AlterID: uint16(option.AlterID), 65 | Security: security, 66 | TLS: option.TLS, 67 | HostName: option.Server, 68 | Port: strconv.Itoa(option.Port), 69 | NetWork: option.Network, 70 | WebSocketPath: option.WSPath, 71 | WebSocketHeaders: option.WSHeaders, 72 | SkipCertVerify: option.SkipCertVerify, 73 | SessionCache: getClientSessionCache(), 74 | }) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | return &Vmess{ 80 | Base: &Base{ 81 | name: option.Name, 82 | tp: C.Vmess, 83 | udp: true, 84 | }, 85 | server: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 86 | client: client, 87 | }, nil 88 | } 89 | 90 | func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr { 91 | var addrType byte 92 | var addr []byte 93 | switch metadata.AddrType { 94 | case C.AtypIPv4: 95 | addrType = byte(vmess.AtypIPv4) 96 | addr = make([]byte, net.IPv4len) 97 | copy(addr[:], metadata.DstIP.To4()) 98 | case C.AtypIPv6: 99 | addrType = byte(vmess.AtypIPv6) 100 | addr = make([]byte, net.IPv6len) 101 | copy(addr[:], metadata.DstIP.To16()) 102 | case C.AtypDomainName: 103 | addrType = byte(vmess.AtypDomainName) 104 | addr = make([]byte, len(metadata.Host)+1) 105 | addr[0] = byte(len(metadata.Host)) 106 | copy(addr[1:], []byte(metadata.Host)) 107 | } 108 | 109 | port, _ := strconv.Atoi(metadata.DstPort) 110 | return &vmess.DstAddr{ 111 | UDP: metadata.NetWork == C.UDP, 112 | AddrType: addrType, 113 | Addr: addr, 114 | Port: uint(port), 115 | } 116 | } 117 | 118 | type vmessUDPConn struct { 119 | net.Conn 120 | } 121 | 122 | func (uc *vmessUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) { 123 | return uc.Conn.Write(b) 124 | } 125 | 126 | func (uc *vmessUDPConn) ReadFrom(b []byte) (int, net.Addr, error) { 127 | n, err := uc.Conn.Read(b) 128 | return n, uc.RemoteAddr(), err 129 | } 130 | -------------------------------------------------------------------------------- /build4android/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Build: 4 | # - git clone -b dev https://github.com/zu1k/clashr 5 | # - cd clashr 6 | # - ANDROID_NDK=/path/to/android/ndk /path/to/this/script 7 | # 8 | 9 | export ANDROID_NDK=/home/king/Android/Sdk/ndk/20.0.5594570 10 | # export GOPATH=/usr/lib/go 11 | 12 | NAME=clashr 13 | BINDIR=bin 14 | VERSION=$(git describe --tags || echo "unknown version") 15 | BUILDTIME=$(LANG=C date -u) 16 | cd .. 17 | 18 | ANDROID_CC=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang 19 | ANDROID_CXX=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++ 20 | ANDROID_LD=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld 21 | export GOARCH=arm64 22 | export GOOS=android 23 | export CXX=$ANDROID_CXX 24 | export CC=$ANDROID_CC 25 | export LD=$ANDROID_LD 26 | export CGO_ENABLED=1 27 | go build -ldflags "-X \"github.com/zu1k/clashr/constant.Version=$VERSION\" -X \"github.com/zu1k/clashr/constant.BuildTime=$BUILDTIME\" -w -s" \ 28 | -o "build4android/clashr_arm64" 29 | 30 | 31 | ANDROID_CC=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang 32 | ANDROID_CXX=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang++ 33 | ANDROID_LD=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-android-ld 34 | export GOARCH=arm 35 | export GOOS=android 36 | export CXX=$ANDROID_CXX 37 | export CC=$ANDROID_CC 38 | export LD=$ANDROID_LD 39 | export CGO_ENABLED=1 40 | go build -ldflags "-X \"github.com/zu1k/clashr/constant.Version=$VERSION\" -X \"github.com/zu1k/clashr/constant.BuildTime=$BUILDTIME\" -w -s" \ 41 | -o "build4android/clashr_armv7a" 42 | 43 | 44 | ANDROID_CC=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang 45 | ANDROID_CXX=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang++ 46 | ANDROID_LD=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android-ld 47 | export GOOS=android 48 | export CXX=$ANDROID_CXX 49 | export CC=$ANDROID_CC 50 | export LD=$ANDROID_LD 51 | export CGO_ENABLED=1 52 | export GOARCH=386 53 | go build -ldflags "-X \"github.com/zu1k/clashr/constant.Version=$VERSION\" -X \"github.com/zu1k/clashr/constant.BuildTime=$BUILDTIME\" -w -s" \ 54 | -o "build4android/clashr_x86" 55 | 56 | 57 | ANDROID_CC=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang 58 | ANDROID_CXX=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang++ 59 | ANDROID_LD=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android-ld 60 | export GOOS=android 61 | export CXX=$ANDROID_CXX 62 | export CC=$ANDROID_CC 63 | export LD=$ANDROID_LD 64 | export CGO_ENABLED=1 65 | export GOARCH=amd64 66 | go build -ldflags "-X \"github.com/zu1k/clashr/constant.Version=$VERSION\" -X \"github.com/zu1k/clashr/constant.BuildTime=$BUILDTIME\" -w -s" \ 67 | -o "build4android/clashr_amd64" 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /common/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Cache store element with a expired time 10 | type Cache struct { 11 | *cache 12 | } 13 | 14 | type cache struct { 15 | mapping sync.Map 16 | janitor *janitor 17 | } 18 | 19 | type element struct { 20 | Expired time.Time 21 | Payload interface{} 22 | } 23 | 24 | // Put element in Cache with its ttl 25 | func (c *cache) Put(key interface{}, payload interface{}, ttl time.Duration) { 26 | c.mapping.Store(key, &element{ 27 | Payload: payload, 28 | Expired: time.Now().Add(ttl), 29 | }) 30 | } 31 | 32 | // Get element in Cache, and drop when it expired 33 | func (c *cache) Get(key interface{}) interface{} { 34 | item, exist := c.mapping.Load(key) 35 | if !exist { 36 | return nil 37 | } 38 | elm := item.(*element) 39 | // expired 40 | if time.Since(elm.Expired) > 0 { 41 | c.mapping.Delete(key) 42 | return nil 43 | } 44 | return elm.Payload 45 | } 46 | 47 | // GetWithExpire element in Cache with Expire Time 48 | func (c *cache) GetWithExpire(key interface{}) (payload interface{}, expired time.Time) { 49 | item, exist := c.mapping.Load(key) 50 | if !exist { 51 | return 52 | } 53 | elm := item.(*element) 54 | // expired 55 | if time.Since(elm.Expired) > 0 { 56 | c.mapping.Delete(key) 57 | return 58 | } 59 | return elm.Payload, elm.Expired 60 | } 61 | 62 | func (c *cache) cleanup() { 63 | c.mapping.Range(func(k, v interface{}) bool { 64 | key := k.(string) 65 | elm := v.(*element) 66 | if time.Since(elm.Expired) > 0 { 67 | c.mapping.Delete(key) 68 | } 69 | return true 70 | }) 71 | } 72 | 73 | type janitor struct { 74 | interval time.Duration 75 | stop chan struct{} 76 | } 77 | 78 | func (j *janitor) process(c *cache) { 79 | ticker := time.NewTicker(j.interval) 80 | for { 81 | select { 82 | case <-ticker.C: 83 | c.cleanup() 84 | case <-j.stop: 85 | ticker.Stop() 86 | return 87 | } 88 | } 89 | } 90 | 91 | func stopJanitor(c *Cache) { 92 | c.janitor.stop <- struct{}{} 93 | } 94 | 95 | // New return *Cache 96 | func New(interval time.Duration) *Cache { 97 | j := &janitor{ 98 | interval: interval, 99 | stop: make(chan struct{}), 100 | } 101 | c := &cache{janitor: j} 102 | go j.process(c) 103 | C := &Cache{c} 104 | runtime.SetFinalizer(C, stopJanitor) 105 | return C 106 | } 107 | -------------------------------------------------------------------------------- /common/cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCache_Basic(t *testing.T) { 12 | interval := 200 * time.Millisecond 13 | ttl := 20 * time.Millisecond 14 | c := New(interval) 15 | c.Put("int", 1, ttl) 16 | c.Put("string", "a", ttl) 17 | 18 | i := c.Get("int") 19 | assert.Equal(t, i.(int), 1, "should recv 1") 20 | 21 | s := c.Get("string") 22 | assert.Equal(t, s.(string), "a", "should recv 'a'") 23 | } 24 | 25 | func TestCache_TTL(t *testing.T) { 26 | interval := 200 * time.Millisecond 27 | ttl := 20 * time.Millisecond 28 | now := time.Now() 29 | c := New(interval) 30 | c.Put("int", 1, ttl) 31 | c.Put("int2", 2, ttl) 32 | 33 | i := c.Get("int") 34 | _, expired := c.GetWithExpire("int2") 35 | assert.Equal(t, i.(int), 1, "should recv 1") 36 | assert.True(t, now.Before(expired)) 37 | 38 | time.Sleep(ttl * 2) 39 | i = c.Get("int") 40 | j, _ := c.GetWithExpire("int2") 41 | assert.Nil(t, i, "should recv nil") 42 | assert.Nil(t, j, "should recv nil") 43 | } 44 | 45 | func TestCache_AutoCleanup(t *testing.T) { 46 | interval := 10 * time.Millisecond 47 | ttl := 15 * time.Millisecond 48 | c := New(interval) 49 | c.Put("int", 1, ttl) 50 | 51 | time.Sleep(ttl * 2) 52 | i := c.Get("int") 53 | j, _ := c.GetWithExpire("int") 54 | assert.Nil(t, i, "should recv nil") 55 | assert.Nil(t, j, "should recv nil") 56 | } 57 | 58 | func TestCache_AutoGC(t *testing.T) { 59 | sign := make(chan struct{}) 60 | go func() { 61 | interval := 10 * time.Millisecond 62 | ttl := 15 * time.Millisecond 63 | c := New(interval) 64 | c.Put("int", 1, ttl) 65 | sign <- struct{}{} 66 | }() 67 | 68 | <-sign 69 | runtime.GC() 70 | } 71 | -------------------------------------------------------------------------------- /common/cache/lrucache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | // Modified by https://github.com/die-net/lrucache 4 | 5 | import ( 6 | "container/list" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // Option is part of Functional Options Pattern 12 | type Option func(*LruCache) 13 | 14 | // WithUpdateAgeOnGet update expires when Get element 15 | func WithUpdateAgeOnGet() Option { 16 | return func(l *LruCache) { 17 | l.updateAgeOnGet = true 18 | } 19 | } 20 | 21 | // WithAge defined element max age (second) 22 | func WithAge(maxAge int64) Option { 23 | return func(l *LruCache) { 24 | l.maxAge = maxAge 25 | } 26 | } 27 | 28 | // WithSize defined max length of LruCache 29 | func WithSize(maxSize int) Option { 30 | return func(l *LruCache) { 31 | l.maxSize = maxSize 32 | } 33 | } 34 | 35 | // LruCache is a thread-safe, in-memory lru-cache that evicts the 36 | // least recently used entries from memory when (if set) the entries are 37 | // older than maxAge (in seconds). Use the New constructor to create one. 38 | type LruCache struct { 39 | maxAge int64 40 | maxSize int 41 | mu sync.Mutex 42 | cache map[interface{}]*list.Element 43 | lru *list.List // Front is least-recent 44 | updateAgeOnGet bool 45 | } 46 | 47 | // NewLRUCache creates an LruCache 48 | func NewLRUCache(options ...Option) *LruCache { 49 | lc := &LruCache{ 50 | lru: list.New(), 51 | cache: make(map[interface{}]*list.Element), 52 | } 53 | 54 | for _, option := range options { 55 | option(lc) 56 | } 57 | 58 | return lc 59 | } 60 | 61 | // Get returns the interface{} representation of a cached response and a bool 62 | // set to true if the key was found. 63 | func (c *LruCache) Get(key interface{}) (interface{}, bool) { 64 | c.mu.Lock() 65 | defer c.mu.Unlock() 66 | 67 | le, ok := c.cache[key] 68 | if !ok { 69 | return nil, false 70 | } 71 | 72 | if c.maxAge > 0 && le.Value.(*entry).expires <= time.Now().Unix() { 73 | c.deleteElement(le) 74 | c.maybeDeleteOldest() 75 | 76 | return nil, false 77 | } 78 | 79 | c.lru.MoveToBack(le) 80 | entry := le.Value.(*entry) 81 | if c.maxAge > 0 && c.updateAgeOnGet { 82 | entry.expires = time.Now().Unix() + c.maxAge 83 | } 84 | value := entry.value 85 | 86 | return value, true 87 | } 88 | 89 | // Exist returns if key exist in cache but not put item to the head of linked list 90 | func (c *LruCache) Exist(key interface{}) bool { 91 | c.mu.Lock() 92 | defer c.mu.Unlock() 93 | 94 | _, ok := c.cache[key] 95 | return ok 96 | } 97 | 98 | // Set stores the interface{} representation of a response for a given key. 99 | func (c *LruCache) Set(key interface{}, value interface{}) { 100 | c.mu.Lock() 101 | defer c.mu.Unlock() 102 | 103 | expires := int64(0) 104 | if c.maxAge > 0 { 105 | expires = time.Now().Unix() + c.maxAge 106 | } 107 | 108 | if le, ok := c.cache[key]; ok { 109 | c.lru.MoveToBack(le) 110 | e := le.Value.(*entry) 111 | e.value = value 112 | e.expires = expires 113 | } else { 114 | e := &entry{key: key, value: value, expires: expires} 115 | c.cache[key] = c.lru.PushBack(e) 116 | 117 | if c.maxSize > 0 { 118 | if len := c.lru.Len(); len > c.maxSize { 119 | c.deleteElement(c.lru.Front()) 120 | } 121 | } 122 | } 123 | 124 | c.maybeDeleteOldest() 125 | } 126 | 127 | // Delete removes the value associated with a key. 128 | func (c *LruCache) Delete(key string) { 129 | c.mu.Lock() 130 | 131 | if le, ok := c.cache[key]; ok { 132 | c.deleteElement(le) 133 | } 134 | 135 | c.mu.Unlock() 136 | } 137 | 138 | func (c *LruCache) maybeDeleteOldest() { 139 | if c.maxAge > 0 { 140 | now := time.Now().Unix() 141 | for le := c.lru.Front(); le != nil && le.Value.(*entry).expires <= now; le = c.lru.Front() { 142 | c.deleteElement(le) 143 | } 144 | } 145 | } 146 | 147 | func (c *LruCache) deleteElement(le *list.Element) { 148 | c.lru.Remove(le) 149 | e := le.Value.(*entry) 150 | delete(c.cache, e.key) 151 | } 152 | 153 | type entry struct { 154 | key interface{} 155 | value interface{} 156 | expires int64 157 | } 158 | -------------------------------------------------------------------------------- /common/cache/lrucache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var entries = []struct { 11 | key string 12 | value string 13 | }{ 14 | {"1", "one"}, 15 | {"2", "two"}, 16 | {"3", "three"}, 17 | {"4", "four"}, 18 | {"5", "five"}, 19 | } 20 | 21 | func TestLRUCache(t *testing.T) { 22 | c := NewLRUCache() 23 | 24 | for _, e := range entries { 25 | c.Set(e.key, e.value) 26 | } 27 | 28 | c.Delete("missing") 29 | _, ok := c.Get("missing") 30 | assert.False(t, ok) 31 | 32 | for _, e := range entries { 33 | value, ok := c.Get(e.key) 34 | if assert.True(t, ok) { 35 | assert.Equal(t, e.value, value.(string)) 36 | } 37 | } 38 | 39 | for _, e := range entries { 40 | c.Delete(e.key) 41 | 42 | _, ok := c.Get(e.key) 43 | assert.False(t, ok) 44 | } 45 | } 46 | 47 | func TestLRUMaxAge(t *testing.T) { 48 | c := NewLRUCache(WithAge(86400)) 49 | 50 | now := time.Now().Unix() 51 | expected := now + 86400 52 | 53 | // Add one expired entry 54 | c.Set("foo", "bar") 55 | c.lru.Back().Value.(*entry).expires = now 56 | 57 | // Reset 58 | c.Set("foo", "bar") 59 | e := c.lru.Back().Value.(*entry) 60 | assert.True(t, e.expires >= now) 61 | c.lru.Back().Value.(*entry).expires = now 62 | 63 | // Set a few and verify expiration times 64 | for _, s := range entries { 65 | c.Set(s.key, s.value) 66 | e := c.lru.Back().Value.(*entry) 67 | assert.True(t, e.expires >= expected && e.expires <= expected+10) 68 | } 69 | 70 | // Make sure we can get them all 71 | for _, s := range entries { 72 | _, ok := c.Get(s.key) 73 | assert.True(t, ok) 74 | } 75 | 76 | // Expire all entries 77 | for _, s := range entries { 78 | le, ok := c.cache[s.key] 79 | if assert.True(t, ok) { 80 | le.Value.(*entry).expires = now 81 | } 82 | } 83 | 84 | // Get one expired entry, which should clear all expired entries 85 | _, ok := c.Get("3") 86 | assert.False(t, ok) 87 | assert.Equal(t, c.lru.Len(), 0) 88 | } 89 | 90 | func TestLRUpdateOnGet(t *testing.T) { 91 | c := NewLRUCache(WithAge(86400), WithUpdateAgeOnGet()) 92 | 93 | now := time.Now().Unix() 94 | expires := now + 86400/2 95 | 96 | // Add one expired entry 97 | c.Set("foo", "bar") 98 | c.lru.Back().Value.(*entry).expires = expires 99 | 100 | _, ok := c.Get("foo") 101 | assert.True(t, ok) 102 | assert.True(t, c.lru.Back().Value.(*entry).expires > expires) 103 | } 104 | 105 | func TestMaxSize(t *testing.T) { 106 | c := NewLRUCache(WithSize(2)) 107 | // Add one expired entry 108 | c.Set("foo", "bar") 109 | _, ok := c.Get("foo") 110 | assert.True(t, ok) 111 | 112 | c.Set("bar", "foo") 113 | c.Set("baz", "foo") 114 | 115 | _, ok = c.Get("foo") 116 | assert.False(t, ok) 117 | } 118 | -------------------------------------------------------------------------------- /common/murmur3/murmur.go: -------------------------------------------------------------------------------- 1 | package murmur3 2 | 3 | type bmixer interface { 4 | bmix(p []byte) (tail []byte) 5 | Size() (n int) 6 | reset() 7 | } 8 | 9 | type digest struct { 10 | clen int // Digested input cumulative length. 11 | tail []byte // 0 to Size()-1 bytes view of `buf'. 12 | buf [16]byte // Expected (but not required) to be Size() large. 13 | seed uint32 // Seed for initializing the hash. 14 | bmixer 15 | } 16 | 17 | func (d *digest) BlockSize() int { return 1 } 18 | 19 | func (d *digest) Write(p []byte) (n int, err error) { 20 | n = len(p) 21 | d.clen += n 22 | 23 | if len(d.tail) > 0 { 24 | // Stick back pending bytes. 25 | nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1]. 26 | if nfree < len(p) { 27 | // One full block can be formed. 28 | block := append(d.tail, p[:nfree]...) 29 | p = p[nfree:] 30 | _ = d.bmix(block) // No tail. 31 | } else { 32 | // Tail's buf is large enough to prevent reallocs. 33 | p = append(d.tail, p...) 34 | } 35 | } 36 | 37 | d.tail = d.bmix(p) 38 | 39 | // Keep own copy of the 0 to Size()-1 pending bytes. 40 | nn := copy(d.buf[:], d.tail) 41 | d.tail = d.buf[:nn] 42 | 43 | return n, nil 44 | } 45 | 46 | func (d *digest) Reset() { 47 | d.clen = 0 48 | d.tail = nil 49 | d.bmixer.reset() 50 | } 51 | -------------------------------------------------------------------------------- /common/murmur3/murmur32.go: -------------------------------------------------------------------------------- 1 | package murmur3 2 | 3 | // https://github.com/spaolacci/murmur3/blob/master/murmur32.go 4 | 5 | import ( 6 | "hash" 7 | "math/bits" 8 | "unsafe" 9 | ) 10 | 11 | // Make sure interfaces are correctly implemented. 12 | var ( 13 | _ hash.Hash32 = new(digest32) 14 | _ bmixer = new(digest32) 15 | ) 16 | 17 | const ( 18 | c1_32 uint32 = 0xcc9e2d51 19 | c2_32 uint32 = 0x1b873593 20 | ) 21 | 22 | // digest32 represents a partial evaluation of a 32 bites hash. 23 | type digest32 struct { 24 | digest 25 | h1 uint32 // Unfinalized running hash. 26 | } 27 | 28 | // New32 returns new 32-bit hasher 29 | func New32() hash.Hash32 { return New32WithSeed(0) } 30 | 31 | // New32WithSeed returns new 32-bit hasher set with explicit seed value 32 | func New32WithSeed(seed uint32) hash.Hash32 { 33 | d := new(digest32) 34 | d.seed = seed 35 | d.bmixer = d 36 | d.Reset() 37 | return d 38 | } 39 | 40 | func (d *digest32) Size() int { return 4 } 41 | 42 | func (d *digest32) reset() { d.h1 = d.seed } 43 | 44 | func (d *digest32) Sum(b []byte) []byte { 45 | h := d.Sum32() 46 | return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h)) 47 | } 48 | 49 | // Digest as many blocks as possible. 50 | func (d *digest32) bmix(p []byte) (tail []byte) { 51 | h1 := d.h1 52 | 53 | nblocks := len(p) / 4 54 | for i := 0; i < nblocks; i++ { 55 | k1 := *(*uint32)(unsafe.Pointer(&p[i*4])) 56 | 57 | k1 *= c1_32 58 | k1 = bits.RotateLeft32(k1, 15) 59 | k1 *= c2_32 60 | 61 | h1 ^= k1 62 | h1 = bits.RotateLeft32(h1, 13) 63 | h1 = h1*4 + h1 + 0xe6546b64 64 | } 65 | d.h1 = h1 66 | return p[nblocks*d.Size():] 67 | } 68 | 69 | func (d *digest32) Sum32() (h1 uint32) { 70 | 71 | h1 = d.h1 72 | 73 | var k1 uint32 74 | switch len(d.tail) & 3 { 75 | case 3: 76 | k1 ^= uint32(d.tail[2]) << 16 77 | fallthrough 78 | case 2: 79 | k1 ^= uint32(d.tail[1]) << 8 80 | fallthrough 81 | case 1: 82 | k1 ^= uint32(d.tail[0]) 83 | k1 *= c1_32 84 | k1 = bits.RotateLeft32(k1, 15) 85 | k1 *= c2_32 86 | h1 ^= k1 87 | } 88 | 89 | h1 ^= uint32(d.clen) 90 | 91 | h1 ^= h1 >> 16 92 | h1 *= 0x85ebca6b 93 | h1 ^= h1 >> 13 94 | h1 *= 0xc2b2ae35 95 | h1 ^= h1 >> 16 96 | 97 | return h1 98 | } 99 | 100 | func Sum32(data []byte) uint32 { return Sum32WithSeed(data, 0) } 101 | 102 | func Sum32WithSeed(data []byte, seed uint32) uint32 { 103 | h1 := seed 104 | 105 | nblocks := len(data) / 4 106 | for i := 0; i < nblocks; i++ { 107 | k1 := *(*uint32)(unsafe.Pointer(&data[i*4])) 108 | 109 | k1 *= c1_32 110 | k1 = bits.RotateLeft32(k1, 15) 111 | k1 *= c2_32 112 | 113 | h1 ^= k1 114 | h1 = bits.RotateLeft32(h1, 13) 115 | h1 = h1*4 + h1 + 0xe6546b64 116 | } 117 | 118 | tail := data[nblocks*4:] 119 | 120 | var k1 uint32 121 | switch len(tail) & 3 { 122 | case 3: 123 | k1 ^= uint32(tail[2]) << 16 124 | fallthrough 125 | case 2: 126 | k1 ^= uint32(tail[1]) << 8 127 | fallthrough 128 | case 1: 129 | k1 ^= uint32(tail[0]) 130 | k1 *= c1_32 131 | k1 = bits.RotateLeft32(k1, 15) 132 | k1 *= c2_32 133 | h1 ^= k1 134 | } 135 | 136 | h1 ^= uint32(len(data)) 137 | 138 | h1 ^= h1 >> 16 139 | h1 *= 0x85ebca6b 140 | h1 ^= h1 >> 13 141 | h1 *= 0xc2b2ae35 142 | h1 ^= h1 >> 16 143 | 144 | return h1 145 | } 146 | -------------------------------------------------------------------------------- /common/observable/iterable.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | type Iterable <-chan interface{} 4 | -------------------------------------------------------------------------------- /common/observable/observable.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | ) 7 | 8 | type Observable struct { 9 | iterable Iterable 10 | listener *sync.Map 11 | done bool 12 | doneLock sync.RWMutex 13 | } 14 | 15 | func (o *Observable) process() { 16 | for item := range o.iterable { 17 | o.listener.Range(func(key, value interface{}) bool { 18 | elm := value.(*Subscriber) 19 | elm.Emit(item) 20 | return true 21 | }) 22 | } 23 | o.close() 24 | } 25 | 26 | func (o *Observable) close() { 27 | o.doneLock.Lock() 28 | o.done = true 29 | o.doneLock.Unlock() 30 | 31 | o.listener.Range(func(key, value interface{}) bool { 32 | elm := value.(*Subscriber) 33 | elm.Close() 34 | return true 35 | }) 36 | } 37 | 38 | func (o *Observable) Subscribe() (Subscription, error) { 39 | o.doneLock.RLock() 40 | done := o.done 41 | o.doneLock.RUnlock() 42 | if done == true { 43 | return nil, errors.New("Observable is closed") 44 | } 45 | subscriber := newSubscriber() 46 | o.listener.Store(subscriber.Out(), subscriber) 47 | return subscriber.Out(), nil 48 | } 49 | 50 | func (o *Observable) UnSubscribe(sub Subscription) { 51 | elm, exist := o.listener.Load(sub) 52 | if !exist { 53 | return 54 | } 55 | subscriber := elm.(*Subscriber) 56 | o.listener.Delete(subscriber.Out()) 57 | subscriber.Close() 58 | } 59 | 60 | func NewObservable(any Iterable) *Observable { 61 | observable := &Observable{ 62 | iterable: any, 63 | listener: &sync.Map{}, 64 | } 65 | go observable.process() 66 | return observable 67 | } 68 | -------------------------------------------------------------------------------- /common/observable/observable_test.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func iterator(item []interface{}) chan interface{} { 11 | ch := make(chan interface{}) 12 | go func() { 13 | time.Sleep(100 * time.Millisecond) 14 | for _, elm := range item { 15 | ch <- elm 16 | } 17 | close(ch) 18 | }() 19 | return ch 20 | } 21 | 22 | func TestObservable(t *testing.T) { 23 | iter := iterator([]interface{}{1, 2, 3, 4, 5}) 24 | src := NewObservable(iter) 25 | data, err := src.Subscribe() 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | count := 0 30 | for range data { 31 | count = count + 1 32 | } 33 | if count != 5 { 34 | t.Error("Revc number error") 35 | } 36 | } 37 | 38 | func TestObservable_MutilSubscribe(t *testing.T) { 39 | iter := iterator([]interface{}{1, 2, 3, 4, 5}) 40 | src := NewObservable(iter) 41 | ch1, _ := src.Subscribe() 42 | ch2, _ := src.Subscribe() 43 | count := 0 44 | 45 | var wg sync.WaitGroup 46 | wg.Add(2) 47 | waitCh := func(ch <-chan interface{}) { 48 | for range ch { 49 | count = count + 1 50 | } 51 | wg.Done() 52 | } 53 | go waitCh(ch1) 54 | go waitCh(ch2) 55 | wg.Wait() 56 | if count != 10 { 57 | t.Error("Revc number error") 58 | } 59 | } 60 | 61 | func TestObservable_UnSubscribe(t *testing.T) { 62 | iter := iterator([]interface{}{1, 2, 3, 4, 5}) 63 | src := NewObservable(iter) 64 | data, err := src.Subscribe() 65 | if err != nil { 66 | t.Error(err) 67 | } 68 | src.UnSubscribe(data) 69 | _, open := <-data 70 | if open { 71 | t.Error("Revc number error") 72 | } 73 | } 74 | 75 | func TestObservable_SubscribeClosedSource(t *testing.T) { 76 | iter := iterator([]interface{}{1}) 77 | src := NewObservable(iter) 78 | data, _ := src.Subscribe() 79 | <-data 80 | 81 | _, closed := src.Subscribe() 82 | if closed == nil { 83 | t.Error("Observable should be closed") 84 | } 85 | } 86 | 87 | func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) { 88 | sub := Subscription(make(chan interface{})) 89 | iter := iterator([]interface{}{1}) 90 | src := NewObservable(iter) 91 | src.UnSubscribe(sub) 92 | } 93 | 94 | func TestObservable_SubscribeGoroutineLeak(t *testing.T) { 95 | // waiting for other goroutine recycle 96 | time.Sleep(120 * time.Millisecond) 97 | init := runtime.NumGoroutine() 98 | iter := iterator([]interface{}{1, 2, 3, 4, 5}) 99 | src := NewObservable(iter) 100 | max := 100 101 | 102 | var list []Subscription 103 | for i := 0; i < max; i++ { 104 | ch, _ := src.Subscribe() 105 | list = append(list, ch) 106 | } 107 | 108 | var wg sync.WaitGroup 109 | wg.Add(max) 110 | waitCh := func(ch <-chan interface{}) { 111 | for range ch { 112 | } 113 | wg.Done() 114 | } 115 | 116 | for _, ch := range list { 117 | go waitCh(ch) 118 | } 119 | wg.Wait() 120 | now := runtime.NumGoroutine() 121 | if init != now { 122 | t.Errorf("Goroutine Leak: init %d now %d", init, now) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /common/observable/subscriber.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "gopkg.in/eapache/channels.v1" 8 | ) 9 | 10 | type Subscription <-chan interface{} 11 | 12 | type Subscriber struct { 13 | buffer *channels.InfiniteChannel 14 | once sync.Once 15 | } 16 | 17 | func (s *Subscriber) Emit(item interface{}) { 18 | defer func() { 19 | if err := recover(); err != nil { 20 | fmt.Println("send on closed channel panic recover") 21 | } 22 | }() 23 | s.buffer.In() <- item 24 | } 25 | 26 | func (s *Subscriber) Out() Subscription { 27 | return s.buffer.Out() 28 | } 29 | 30 | func (s *Subscriber) Close() { 31 | s.once.Do(func() { 32 | s.buffer.Close() 33 | }) 34 | } 35 | 36 | func newSubscriber() *Subscriber { 37 | sub := &Subscriber{ 38 | buffer: channels.NewInfiniteChannel(), 39 | } 40 | return sub 41 | } 42 | -------------------------------------------------------------------------------- /common/picker/picker.go: -------------------------------------------------------------------------------- 1 | package picker 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Picker provides synchronization, and Context cancelation 10 | // for groups of goroutines working on subtasks of a common task. 11 | // Inspired by errGroup 12 | type Picker struct { 13 | ctx context.Context 14 | cancel func() 15 | 16 | wg sync.WaitGroup 17 | 18 | once sync.Once 19 | result interface{} 20 | 21 | firstDone chan struct{} 22 | } 23 | 24 | func newPicker(ctx context.Context, cancel func()) *Picker { 25 | return &Picker{ 26 | ctx: ctx, 27 | cancel: cancel, 28 | firstDone: make(chan struct{}, 1), 29 | } 30 | } 31 | 32 | // WithContext returns a new Picker and an associated Context derived from ctx. 33 | // and cancel when first element return. 34 | func WithContext(ctx context.Context) (*Picker, context.Context) { 35 | ctx, cancel := context.WithCancel(ctx) 36 | return newPicker(ctx, cancel), ctx 37 | } 38 | 39 | // WithTimeout returns a new Picker and an associated Context derived from ctx with timeout. 40 | func WithTimeout(ctx context.Context, timeout time.Duration) (*Picker, context.Context) { 41 | ctx, cancel := context.WithTimeout(ctx, timeout) 42 | return newPicker(ctx, cancel), ctx 43 | } 44 | 45 | // WithoutAutoCancel returns a new Picker and an associated Context derived from ctx, 46 | // but it wouldn't cancel context when the first element return. 47 | func WithoutAutoCancel(ctx context.Context) *Picker { 48 | return newPicker(ctx, nil) 49 | } 50 | 51 | // Wait blocks until all function calls from the Go method have returned, 52 | // then returns the first nil error result (if any) from them. 53 | func (p *Picker) Wait() interface{} { 54 | p.wg.Wait() 55 | if p.cancel != nil { 56 | p.cancel() 57 | } 58 | return p.result 59 | } 60 | 61 | // WaitWithoutCancel blocks until the first result return, if timeout will return nil. 62 | func (p *Picker) WaitWithoutCancel() interface{} { 63 | select { 64 | case <-p.firstDone: 65 | return p.result 66 | case <-p.ctx.Done(): 67 | return p.result 68 | } 69 | } 70 | 71 | // Go calls the given function in a new goroutine. 72 | // The first call to return a nil error cancels the group; its result will be returned by Wait. 73 | func (p *Picker) Go(f func() (interface{}, error)) { 74 | p.wg.Add(1) 75 | 76 | go func() { 77 | defer p.wg.Done() 78 | 79 | if ret, err := f(); err == nil { 80 | p.once.Do(func() { 81 | p.result = ret 82 | p.firstDone <- struct{}{} 83 | if p.cancel != nil { 84 | p.cancel() 85 | } 86 | }) 87 | } 88 | }() 89 | } 90 | -------------------------------------------------------------------------------- /common/picker/picker_test.go: -------------------------------------------------------------------------------- 1 | package picker 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func sleepAndSend(ctx context.Context, delay int, input interface{}) func() (interface{}, error) { 12 | return func() (interface{}, error) { 13 | timer := time.NewTimer(time.Millisecond * time.Duration(delay)) 14 | select { 15 | case <-timer.C: 16 | return input, nil 17 | case <-ctx.Done(): 18 | return nil, ctx.Err() 19 | } 20 | } 21 | } 22 | 23 | func TestPicker_Basic(t *testing.T) { 24 | picker, ctx := WithContext(context.Background()) 25 | picker.Go(sleepAndSend(ctx, 30, 2)) 26 | picker.Go(sleepAndSend(ctx, 20, 1)) 27 | 28 | number := picker.Wait() 29 | assert.NotNil(t, number) 30 | assert.Equal(t, number.(int), 1) 31 | } 32 | 33 | func TestPicker_Timeout(t *testing.T) { 34 | picker, ctx := WithTimeout(context.Background(), time.Millisecond*5) 35 | picker.Go(sleepAndSend(ctx, 20, 1)) 36 | 37 | number := picker.Wait() 38 | assert.Nil(t, number) 39 | } 40 | 41 | func TestPicker_WaitWithoutAutoCancel(t *testing.T) { 42 | ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*60) 43 | defer cancel() 44 | picker := WithoutAutoCancel(ctx) 45 | 46 | trigger := false 47 | picker.Go(sleepAndSend(ctx, 10, 1)) 48 | picker.Go(func() (interface{}, error) { 49 | timer := time.NewTimer(time.Millisecond * time.Duration(30)) 50 | select { 51 | case <-timer.C: 52 | trigger = true 53 | return 2, nil 54 | case <-ctx.Done(): 55 | return nil, ctx.Err() 56 | } 57 | }) 58 | elm := picker.WaitWithoutCancel() 59 | 60 | assert.NotNil(t, elm) 61 | assert.Equal(t, elm.(int), 1) 62 | 63 | elm = picker.Wait() 64 | assert.True(t, trigger) 65 | assert.Equal(t, elm.(int), 1) 66 | } 67 | -------------------------------------------------------------------------------- /common/pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | const ( 8 | // io.Copy default buffer size is 32 KiB 9 | // but the maximum packet size of vmess/shadowsocks is about 16 KiB 10 | // so define a buffer of 20 KiB to reduce the memory of each TCP relay 11 | bufferSize = 32 * 1024 12 | ) 13 | 14 | // BufPool provide buffer for relay 15 | var BufPool = sync.Pool{New: func() interface{} { return make([]byte, bufferSize) }} 16 | -------------------------------------------------------------------------------- /common/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Queue is a simple concurrent safe queue 8 | type Queue struct { 9 | items []interface{} 10 | lock sync.RWMutex 11 | } 12 | 13 | // Put add the item to the queue. 14 | func (q *Queue) Put(items ...interface{}) { 15 | if len(items) == 0 { 16 | return 17 | } 18 | 19 | q.lock.Lock() 20 | q.items = append(q.items, items...) 21 | q.lock.Unlock() 22 | } 23 | 24 | // Pop returns the head of items. 25 | func (q *Queue) Pop() interface{} { 26 | if len(q.items) == 0 { 27 | return nil 28 | } 29 | 30 | q.lock.Lock() 31 | head := q.items[0] 32 | q.items = q.items[1:] 33 | q.lock.Unlock() 34 | return head 35 | } 36 | 37 | // Last returns the last of item. 38 | func (q *Queue) Last() interface{} { 39 | if len(q.items) == 0 { 40 | return nil 41 | } 42 | 43 | q.lock.RLock() 44 | last := q.items[len(q.items)-1] 45 | q.lock.RUnlock() 46 | return last 47 | } 48 | 49 | // Copy get the copy of queue. 50 | func (q *Queue) Copy() []interface{} { 51 | items := []interface{}{} 52 | q.lock.RLock() 53 | items = append(items, q.items...) 54 | q.lock.RUnlock() 55 | return items 56 | } 57 | 58 | // Len returns the number of items in this queue. 59 | func (q *Queue) Len() int64 { 60 | q.lock.Lock() 61 | defer q.lock.Unlock() 62 | 63 | return int64(len(q.items)) 64 | } 65 | 66 | // New is a constructor for a new concurrent safe queue. 67 | func New(hint int64) *Queue { 68 | return &Queue{ 69 | items: make([]interface{}, 0, hint), 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/structure/structure_test.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | var decoder = NewDecoder(Option{TagName: "test"}) 9 | var weakTypeDecoder = NewDecoder(Option{TagName: "test", WeaklyTypedInput: true}) 10 | 11 | type Baz struct { 12 | Foo int `test:"foo"` 13 | Bar string `test:"bar"` 14 | } 15 | 16 | type BazSlice struct { 17 | Foo int `test:"foo"` 18 | Bar []string `test:"bar"` 19 | } 20 | 21 | type BazOptional struct { 22 | Foo int `test:"foo,omitempty"` 23 | Bar string `test:"bar,omitempty"` 24 | } 25 | 26 | func TestStructure_Basic(t *testing.T) { 27 | rawMap := map[string]interface{}{ 28 | "foo": 1, 29 | "bar": "test", 30 | "extra": false, 31 | } 32 | 33 | goal := &Baz{ 34 | Foo: 1, 35 | Bar: "test", 36 | } 37 | 38 | s := &Baz{} 39 | err := decoder.Decode(rawMap, s) 40 | if err != nil { 41 | t.Fatal(err.Error()) 42 | } 43 | if !reflect.DeepEqual(s, goal) { 44 | t.Fatalf("bad: %#v", s) 45 | } 46 | } 47 | 48 | func TestStructure_Slice(t *testing.T) { 49 | rawMap := map[string]interface{}{ 50 | "foo": 1, 51 | "bar": []string{"one", "two"}, 52 | } 53 | 54 | goal := &BazSlice{ 55 | Foo: 1, 56 | Bar: []string{"one", "two"}, 57 | } 58 | 59 | s := &BazSlice{} 60 | err := decoder.Decode(rawMap, s) 61 | if err != nil { 62 | t.Fatal(err.Error()) 63 | } 64 | if !reflect.DeepEqual(s, goal) { 65 | t.Fatalf("bad: %#v", s) 66 | } 67 | } 68 | 69 | func TestStructure_Optional(t *testing.T) { 70 | rawMap := map[string]interface{}{ 71 | "foo": 1, 72 | } 73 | 74 | goal := &BazOptional{ 75 | Foo: 1, 76 | } 77 | 78 | s := &BazOptional{} 79 | err := decoder.Decode(rawMap, s) 80 | if err != nil { 81 | t.Fatal(err.Error()) 82 | } 83 | if !reflect.DeepEqual(s, goal) { 84 | t.Fatalf("bad: %#v", s) 85 | } 86 | } 87 | 88 | func TestStructure_MissingKey(t *testing.T) { 89 | rawMap := map[string]interface{}{ 90 | "foo": 1, 91 | } 92 | 93 | s := &Baz{} 94 | err := decoder.Decode(rawMap, s) 95 | if err == nil { 96 | t.Fatalf("should throw error: %#v", s) 97 | } 98 | } 99 | 100 | func TestStructure_ParamError(t *testing.T) { 101 | rawMap := map[string]interface{}{} 102 | s := Baz{} 103 | err := decoder.Decode(rawMap, s) 104 | if err == nil { 105 | t.Fatalf("should throw error: %#v", s) 106 | } 107 | } 108 | 109 | func TestStructure_SliceTypeError(t *testing.T) { 110 | rawMap := map[string]interface{}{ 111 | "foo": 1, 112 | "bar": []int{1, 2}, 113 | } 114 | 115 | s := &BazSlice{} 116 | err := decoder.Decode(rawMap, s) 117 | if err == nil { 118 | t.Fatalf("should throw error: %#v", s) 119 | } 120 | } 121 | 122 | func TestStructure_WeakType(t *testing.T) { 123 | rawMap := map[string]interface{}{ 124 | "foo": "1", 125 | "bar": []int{1}, 126 | } 127 | 128 | goal := &BazSlice{ 129 | Foo: 1, 130 | Bar: []string{"1"}, 131 | } 132 | 133 | s := &BazSlice{} 134 | err := weakTypeDecoder.Decode(rawMap, s) 135 | if err != nil { 136 | t.Fatal(err.Error()) 137 | } 138 | if !reflect.DeepEqual(s, goal) { 139 | t.Fatalf("bad: %#v", s) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /component/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Authenticator interface { 8 | Verify(user string, pass string) bool 9 | Users() []string 10 | } 11 | 12 | type AuthUser struct { 13 | User string 14 | Pass string 15 | } 16 | 17 | type inMemoryAuthenticator struct { 18 | storage *sync.Map 19 | usernames []string 20 | } 21 | 22 | func (au *inMemoryAuthenticator) Verify(user string, pass string) bool { 23 | realPass, ok := au.storage.Load(user) 24 | return ok && realPass == pass 25 | } 26 | 27 | func (au *inMemoryAuthenticator) Users() []string { return au.usernames } 28 | 29 | func NewAuthenticator(users []AuthUser) Authenticator { 30 | if len(users) == 0 { 31 | return nil 32 | } 33 | 34 | au := &inMemoryAuthenticator{storage: &sync.Map{}} 35 | for _, user := range users { 36 | au.storage.Store(user.User, user.Pass) 37 | } 38 | usernames := make([]string, 0, len(users)) 39 | au.storage.Range(func(key, value interface{}) bool { 40 | usernames = append(usernames, key.(string)) 41 | return true 42 | }) 43 | au.usernames = usernames 44 | 45 | return au 46 | } 47 | -------------------------------------------------------------------------------- /component/domain-trie/node.go: -------------------------------------------------------------------------------- 1 | package trie 2 | 3 | // Node is the trie's node 4 | type Node struct { 5 | Data interface{} 6 | children map[string]*Node 7 | } 8 | 9 | func (n *Node) getChild(s string) *Node { 10 | return n.children[s] 11 | } 12 | 13 | func (n *Node) hasChild(s string) bool { 14 | return n.getChild(s) != nil 15 | } 16 | 17 | func (n *Node) addChild(s string, child *Node) { 18 | n.children[s] = child 19 | } 20 | 21 | func newNode(data interface{}) *Node { 22 | return &Node{ 23 | Data: data, 24 | children: map[string]*Node{}, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /component/domain-trie/tire.go: -------------------------------------------------------------------------------- 1 | package trie 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | wildcard = "*" 10 | domainStep = "." 11 | ) 12 | 13 | var ( 14 | // ErrInvalidDomain means insert domain is invalid 15 | ErrInvalidDomain = errors.New("invalid domain") 16 | ) 17 | 18 | // Trie contains the main logic for adding and searching nodes for domain segments. 19 | // support wildcard domain (e.g *.google.com) 20 | type Trie struct { 21 | root *Node 22 | } 23 | 24 | func isValidDomain(domain string) bool { 25 | return domain != "" && domain[0] != '.' && domain[len(domain)-1] != '.' 26 | } 27 | 28 | // Insert adds a node to the trie. 29 | // Support 30 | // 1. www.example.com 31 | // 2. *.example.com 32 | // 3. subdomain.*.example.com 33 | func (t *Trie) Insert(domain string, data interface{}) error { 34 | if !isValidDomain(domain) { 35 | return ErrInvalidDomain 36 | } 37 | 38 | parts := strings.Split(domain, domainStep) 39 | node := t.root 40 | // reverse storage domain part to save space 41 | for i := len(parts) - 1; i >= 0; i-- { 42 | part := parts[i] 43 | if !node.hasChild(part) { 44 | node.addChild(part, newNode(nil)) 45 | } 46 | 47 | node = node.getChild(part) 48 | } 49 | 50 | node.Data = data 51 | return nil 52 | } 53 | 54 | // Search is the most important part of the Trie. 55 | // Priority as: 56 | // 1. static part 57 | // 2. wildcard domain 58 | func (t *Trie) Search(domain string) *Node { 59 | if !isValidDomain(domain) { 60 | return nil 61 | } 62 | parts := strings.Split(domain, domainStep) 63 | 64 | n := t.root 65 | for i := len(parts) - 1; i >= 0; i-- { 66 | part := parts[i] 67 | 68 | var child *Node 69 | if !n.hasChild(part) { 70 | if !n.hasChild(wildcard) { 71 | return nil 72 | } 73 | 74 | child = n.getChild(wildcard) 75 | } else { 76 | child = n.getChild(part) 77 | } 78 | 79 | n = child 80 | } 81 | 82 | if n.Data == nil { 83 | return nil 84 | } 85 | 86 | return n 87 | } 88 | 89 | // New returns a new, empty Trie. 90 | func New() *Trie { 91 | return &Trie{root: newNode(nil)} 92 | } 93 | -------------------------------------------------------------------------------- /component/domain-trie/trie_test.go: -------------------------------------------------------------------------------- 1 | package trie 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | var localIP = net.IP{127, 0, 0, 1} 9 | 10 | func TestTrie_Basic(t *testing.T) { 11 | tree := New() 12 | domains := []string{ 13 | "example.com", 14 | "google.com", 15 | } 16 | 17 | for _, domain := range domains { 18 | tree.Insert(domain, localIP) 19 | } 20 | 21 | node := tree.Search("example.com") 22 | if node == nil { 23 | t.Error("should not recv nil") 24 | } 25 | 26 | if !node.Data.(net.IP).Equal(localIP) { 27 | t.Error("should equal 127.0.0.1") 28 | } 29 | 30 | if tree.Insert("", localIP) == nil { 31 | t.Error("should return error") 32 | } 33 | } 34 | 35 | func TestTrie_Wildcard(t *testing.T) { 36 | tree := New() 37 | domains := []string{ 38 | "*.example.com", 39 | "sub.*.example.com", 40 | "*.dev", 41 | } 42 | 43 | for _, domain := range domains { 44 | tree.Insert(domain, localIP) 45 | } 46 | 47 | if tree.Search("sub.example.com") == nil { 48 | t.Error("should not recv nil") 49 | } 50 | 51 | if tree.Search("sub.foo.example.com") == nil { 52 | t.Error("should not recv nil") 53 | } 54 | 55 | if tree.Search("foo.sub.example.com") != nil { 56 | t.Error("should recv nil") 57 | } 58 | 59 | if tree.Search("foo.example.dev") != nil { 60 | t.Error("should recv nil") 61 | } 62 | 63 | if tree.Search("example.com") != nil { 64 | t.Error("should recv nil") 65 | } 66 | } 67 | 68 | func TestTrie_Boundary(t *testing.T) { 69 | tree := New() 70 | tree.Insert("*.dev", localIP) 71 | 72 | if err := tree.Insert(".", localIP); err == nil { 73 | t.Error("should recv err") 74 | } 75 | 76 | if err := tree.Insert(".com", localIP); err == nil { 77 | t.Error("should recv err") 78 | } 79 | 80 | if tree.Search("dev") != nil { 81 | t.Error("should recv nil") 82 | } 83 | 84 | if tree.Search(".dev") != nil { 85 | t.Error("should recv nil") 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /component/fakeip/pool.go: -------------------------------------------------------------------------------- 1 | package fakeip 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "sync" 7 | 8 | "github.com/zu1k/clashr/common/cache" 9 | ) 10 | 11 | // Pool is a implementation about fake ip generator without storage 12 | type Pool struct { 13 | max uint32 14 | min uint32 15 | gateway uint32 16 | offset uint32 17 | mux *sync.Mutex 18 | cache *cache.LruCache 19 | } 20 | 21 | // Lookup return a fake ip with host 22 | func (p *Pool) Lookup(host string) net.IP { 23 | p.mux.Lock() 24 | defer p.mux.Unlock() 25 | if elm, exist := p.cache.Get(host); exist { 26 | ip := elm.(net.IP) 27 | 28 | // ensure ip --> host on head of linked list 29 | n := ipToUint(ip.To4()) 30 | offset := n - p.min + 1 31 | p.cache.Get(offset) 32 | return ip 33 | } 34 | 35 | ip := p.get(host) 36 | p.cache.Set(host, ip) 37 | return ip 38 | } 39 | 40 | // LookBack return host with the fake ip 41 | func (p *Pool) LookBack(ip net.IP) (string, bool) { 42 | p.mux.Lock() 43 | defer p.mux.Unlock() 44 | 45 | if ip = ip.To4(); ip == nil { 46 | return "", false 47 | } 48 | 49 | n := ipToUint(ip.To4()) 50 | offset := n - p.min + 1 51 | 52 | if elm, exist := p.cache.Get(offset); exist { 53 | host := elm.(string) 54 | 55 | // ensure host --> ip on head of linked list 56 | p.cache.Get(host) 57 | return host, true 58 | } 59 | 60 | return "", false 61 | } 62 | 63 | // Gateway return gateway ip 64 | func (p *Pool) Gateway() net.IP { 65 | return uintToIP(p.gateway) 66 | } 67 | 68 | func (p *Pool) get(host string) net.IP { 69 | current := p.offset 70 | for { 71 | p.offset = (p.offset + 1) % (p.max - p.min) 72 | // Avoid infinite loops 73 | if p.offset == current { 74 | break 75 | } 76 | 77 | if !p.cache.Exist(p.offset) { 78 | break 79 | } 80 | } 81 | ip := uintToIP(p.min + p.offset - 1) 82 | p.cache.Set(p.offset, host) 83 | return ip 84 | } 85 | 86 | func ipToUint(ip net.IP) uint32 { 87 | v := uint32(ip[0]) << 24 88 | v += uint32(ip[1]) << 16 89 | v += uint32(ip[2]) << 8 90 | v += uint32(ip[3]) 91 | return v 92 | } 93 | 94 | func uintToIP(v uint32) net.IP { 95 | return net.IPv4(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 96 | } 97 | 98 | // New return Pool instance 99 | func New(ipnet *net.IPNet, size int) (*Pool, error) { 100 | min := ipToUint(ipnet.IP) + 2 101 | 102 | ones, bits := ipnet.Mask.Size() 103 | total := 1< n { 52 | ho.buf = buf[:idx+4+length] 53 | ho.offset = idx + 4 + n 54 | } else { 55 | pool.BufPool.Put(buf[:cap(buf)]) 56 | } 57 | return n, nil 58 | } 59 | return ho.Conn.Read(b) 60 | } 61 | 62 | func (ho *HTTPObfs) Write(b []byte) (int, error) { 63 | if ho.firstRequest { 64 | randBytes := make([]byte, 16) 65 | rand.Read(randBytes) 66 | req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) 67 | req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2)) 68 | req.Header.Set("Upgrade", "websocket") 69 | req.Header.Set("Connection", "Upgrade") 70 | req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port) 71 | req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes)) 72 | req.ContentLength = int64(len(b)) 73 | err := req.Write(ho.Conn) 74 | ho.firstRequest = false 75 | return len(b), err 76 | } 77 | 78 | return ho.Conn.Write(b) 79 | } 80 | 81 | // NewHTTPObfs return a HTTPObfs 82 | func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn { 83 | return &HTTPObfs{ 84 | Conn: conn, 85 | firstRequest: true, 86 | firstResponse: true, 87 | host: host, 88 | port: port, 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /component/simple-obfs/tls.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "math/rand" 8 | "net" 9 | "time" 10 | 11 | "github.com/zu1k/clashr/common/pool" 12 | ) 13 | 14 | func init() { 15 | rand.Seed(time.Now().Unix()) 16 | } 17 | 18 | const ( 19 | chunkSize = 1 << 14 // 2 ** 14 == 16 * 1024 20 | ) 21 | 22 | // TLSObfs is shadowsocks tls simple-obfs implementation 23 | type TLSObfs struct { 24 | net.Conn 25 | server string 26 | remain int 27 | firstRequest bool 28 | firstResponse bool 29 | } 30 | 31 | func (to *TLSObfs) read(b []byte, discardN int) (int, error) { 32 | buf := pool.BufPool.Get().([]byte) 33 | _, err := io.ReadFull(to.Conn, buf[:discardN]) 34 | if err != nil { 35 | return 0, err 36 | } 37 | pool.BufPool.Put(buf[:cap(buf)]) 38 | 39 | sizeBuf := make([]byte, 2) 40 | _, err = io.ReadFull(to.Conn, sizeBuf) 41 | if err != nil { 42 | return 0, nil 43 | } 44 | 45 | length := int(binary.BigEndian.Uint16(sizeBuf)) 46 | if length > len(b) { 47 | n, err := to.Conn.Read(b) 48 | if err != nil { 49 | return n, err 50 | } 51 | to.remain = length - n 52 | return n, nil 53 | } 54 | 55 | return io.ReadFull(to.Conn, b[:length]) 56 | } 57 | 58 | func (to *TLSObfs) Read(b []byte) (int, error) { 59 | if to.remain > 0 { 60 | length := to.remain 61 | if length > len(b) { 62 | length = len(b) 63 | } 64 | 65 | n, err := io.ReadFull(to.Conn, b[:length]) 66 | to.remain -= n 67 | return n, err 68 | } 69 | 70 | if to.firstResponse { 71 | // type + ver + lensize + 91 = 96 72 | // type + ver + lensize + 1 = 6 73 | // type + ver = 3 74 | to.firstResponse = false 75 | return to.read(b, 105) 76 | } 77 | 78 | // type + ver = 3 79 | return to.read(b, 3) 80 | } 81 | func (to *TLSObfs) Write(b []byte) (int, error) { 82 | length := len(b) 83 | for i := 0; i < length; i += chunkSize { 84 | end := i + chunkSize 85 | if end > length { 86 | end = length 87 | } 88 | 89 | n, err := to.write(b[i:end]) 90 | if err != nil { 91 | return n, err 92 | } 93 | } 94 | return length, nil 95 | } 96 | 97 | func (to *TLSObfs) write(b []byte) (int, error) { 98 | if to.firstRequest { 99 | helloMsg := makeClientHelloMsg(b, to.server) 100 | _, err := to.Conn.Write(helloMsg) 101 | to.firstRequest = false 102 | return len(b), err 103 | } 104 | 105 | size := pool.BufPool.Get().([]byte) 106 | binary.BigEndian.PutUint16(size[:2], uint16(len(b))) 107 | 108 | buf := &bytes.Buffer{} 109 | buf.Write([]byte{0x17, 0x03, 0x03}) 110 | buf.Write(size[:2]) 111 | buf.Write(b) 112 | _, err := to.Conn.Write(buf.Bytes()) 113 | pool.BufPool.Put(size[:cap(size)]) 114 | return len(b), err 115 | } 116 | 117 | // NewTLSObfs return a SimpleObfs 118 | func NewTLSObfs(conn net.Conn, server string) net.Conn { 119 | return &TLSObfs{ 120 | Conn: conn, 121 | server: server, 122 | firstRequest: true, 123 | firstResponse: true, 124 | } 125 | } 126 | 127 | func makeClientHelloMsg(data []byte, server string) []byte { 128 | random := make([]byte, 28) 129 | sessionID := make([]byte, 32) 130 | rand.Read(random) 131 | rand.Read(sessionID) 132 | 133 | buf := &bytes.Buffer{} 134 | 135 | // handshake, TLS 1.0 version, length 136 | buf.WriteByte(22) 137 | buf.Write([]byte{0x03, 0x01}) 138 | length := uint16(212 + len(data) + len(server)) 139 | buf.WriteByte(byte(length >> 8)) 140 | buf.WriteByte(byte(length & 0xff)) 141 | 142 | // clientHello, length, TLS 1.2 version 143 | buf.WriteByte(1) 144 | buf.WriteByte(0) 145 | binary.Write(buf, binary.BigEndian, uint16(208+len(data)+len(server))) 146 | buf.Write([]byte{0x03, 0x03}) 147 | 148 | // random with timestamp, sid len, sid 149 | binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix())) 150 | buf.Write(random) 151 | buf.WriteByte(32) 152 | buf.Write(sessionID) 153 | 154 | // cipher suites 155 | buf.Write([]byte{0x00, 0x38}) 156 | buf.Write([]byte{ 157 | 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 158 | 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 159 | 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d, 160 | 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 161 | }) 162 | 163 | // compression 164 | buf.Write([]byte{0x01, 0x00}) 165 | 166 | // extension length 167 | binary.Write(buf, binary.BigEndian, uint16(79+len(data)+len(server))) 168 | 169 | // session ticket 170 | buf.Write([]byte{0x00, 0x23}) 171 | binary.Write(buf, binary.BigEndian, uint16(len(data))) 172 | buf.Write(data) 173 | 174 | // server name 175 | buf.Write([]byte{0x00, 0x00}) 176 | binary.Write(buf, binary.BigEndian, uint16(len(server)+5)) 177 | binary.Write(buf, binary.BigEndian, uint16(len(server)+3)) 178 | buf.WriteByte(0) 179 | binary.Write(buf, binary.BigEndian, uint16(len(server))) 180 | buf.Write([]byte(server)) 181 | 182 | // ec_point 183 | buf.Write([]byte{0x00, 0x0b, 0x00, 0x04, 0x03, 0x01, 0x00, 0x02}) 184 | 185 | // groups 186 | buf.Write([]byte{0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18}) 187 | 188 | // signature 189 | buf.Write([]byte{ 190 | 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 191 | 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 192 | 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 193 | }) 194 | 195 | // encrypt then mac 196 | buf.Write([]byte{0x00, 0x16, 0x00, 0x00}) 197 | 198 | // extended master secret 199 | buf.Write([]byte{0x00, 0x17, 0x00, 0x00}) 200 | 201 | return buf.Bytes() 202 | } 203 | -------------------------------------------------------------------------------- /component/snell/cipher.go: -------------------------------------------------------------------------------- 1 | package snell 2 | 3 | import ( 4 | "crypto/cipher" 5 | 6 | "golang.org/x/crypto/argon2" 7 | ) 8 | 9 | type snellCipher struct { 10 | psk []byte 11 | makeAEAD func(key []byte) (cipher.AEAD, error) 12 | } 13 | 14 | func (sc *snellCipher) KeySize() int { return 32 } 15 | func (sc *snellCipher) SaltSize() int { return 16 } 16 | func (sc *snellCipher) Encrypter(salt []byte) (cipher.AEAD, error) { 17 | return sc.makeAEAD(argon2.IDKey(sc.psk, salt, 3, 8, 1, uint32(sc.KeySize()))) 18 | } 19 | func (sc *snellCipher) Decrypter(salt []byte) (cipher.AEAD, error) { 20 | return sc.makeAEAD(argon2.IDKey(sc.psk, salt, 3, 8, 1, uint32(sc.KeySize()))) 21 | } 22 | -------------------------------------------------------------------------------- /component/snell/snell.go: -------------------------------------------------------------------------------- 1 | package snell 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | "net" 9 | "sync" 10 | 11 | "github.com/Dreamacro/go-shadowsocks2/shadowaead" 12 | "golang.org/x/crypto/chacha20poly1305" 13 | ) 14 | 15 | const ( 16 | CommandPing byte = 0 17 | CommandConnect byte = 1 18 | 19 | CommandTunnel byte = 0 20 | CommandError byte = 2 21 | 22 | Version byte = 1 23 | ) 24 | 25 | var ( 26 | bufferPool = sync.Pool{New: func() interface{} { return &bytes.Buffer{} }} 27 | ) 28 | 29 | type Snell struct { 30 | net.Conn 31 | buffer [1]byte 32 | reply bool 33 | } 34 | 35 | func (s *Snell) Read(b []byte) (int, error) { 36 | if s.reply { 37 | return s.Conn.Read(b) 38 | } 39 | 40 | s.reply = true 41 | if _, err := io.ReadFull(s.Conn, s.buffer[:]); err != nil { 42 | return 0, err 43 | } 44 | 45 | if s.buffer[0] == CommandTunnel { 46 | return s.Conn.Read(b) 47 | } else if s.buffer[0] != CommandError { 48 | return 0, errors.New("Command not support") 49 | } 50 | 51 | // CommandError 52 | if _, err := io.ReadFull(s.Conn, s.buffer[:]); err != nil { 53 | return 0, err 54 | } 55 | 56 | length := int(s.buffer[0]) 57 | msg := make([]byte, length) 58 | 59 | if _, err := io.ReadFull(s.Conn, msg); err != nil { 60 | return 0, err 61 | } 62 | 63 | return 0, errors.New(string(msg)) 64 | } 65 | 66 | func WriteHeader(conn net.Conn, host string, port uint) error { 67 | buf := bufferPool.Get().(*bytes.Buffer) 68 | buf.Reset() 69 | defer bufferPool.Put(buf) 70 | buf.WriteByte(Version) 71 | buf.WriteByte(CommandConnect) 72 | 73 | // clientID length & id 74 | buf.WriteByte(0) 75 | 76 | // host & port 77 | buf.WriteByte(uint8(len(host))) 78 | buf.WriteString(host) 79 | binary.Write(buf, binary.BigEndian, uint16(port)) 80 | 81 | if _, err := conn.Write(buf.Bytes()); err != nil { 82 | return err 83 | } 84 | 85 | return nil 86 | } 87 | 88 | func StreamConn(conn net.Conn, psk []byte) net.Conn { 89 | cipher := &snellCipher{psk, chacha20poly1305.New} 90 | return &Snell{Conn: shadowaead.NewConn(conn, cipher)} 91 | } 92 | -------------------------------------------------------------------------------- /component/v2ray-plugin/mux.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | "net" 9 | ) 10 | 11 | type SessionStatus = byte 12 | 13 | const ( 14 | SessionStatusNew SessionStatus = 0x01 15 | SessionStatusKeep SessionStatus = 0x02 16 | SessionStatusEnd SessionStatus = 0x03 17 | SessionStatusKeepAlive SessionStatus = 0x04 18 | ) 19 | 20 | const ( 21 | OptionNone = byte(0x00) 22 | OptionData = byte(0x01) 23 | OptionError = byte(0x02) 24 | ) 25 | 26 | type MuxOption struct { 27 | ID [2]byte 28 | Port uint16 29 | Host string 30 | Type string 31 | } 32 | 33 | // Mux is an mux-compatible client for v2ray-plugin, not a complete implementation 34 | type Mux struct { 35 | net.Conn 36 | buf bytes.Buffer 37 | id [2]byte 38 | length [2]byte 39 | status [2]byte 40 | otb []byte 41 | remain int 42 | } 43 | 44 | func (m *Mux) Read(b []byte) (int, error) { 45 | if m.remain != 0 { 46 | length := m.remain 47 | if len(b) < m.remain { 48 | length = len(b) 49 | } 50 | 51 | n, err := m.Conn.Read(b[:length]) 52 | if err != nil { 53 | return 0, err 54 | } 55 | m.remain = m.remain - n 56 | return n, nil 57 | } 58 | 59 | for { 60 | _, err := io.ReadFull(m.Conn, m.length[:]) 61 | if err != nil { 62 | return 0, err 63 | } 64 | length := binary.BigEndian.Uint16(m.length[:]) 65 | if length > 512 { 66 | return 0, errors.New("invalid metalen") 67 | } 68 | 69 | _, err = io.ReadFull(m.Conn, m.id[:]) 70 | if err != nil { 71 | return 0, err 72 | } 73 | 74 | _, err = m.Conn.Read(m.status[:]) 75 | if err != nil { 76 | return 0, err 77 | } 78 | 79 | opcode := m.status[0] 80 | if opcode == SessionStatusKeepAlive { 81 | continue 82 | } 83 | 84 | opts := m.status[1] 85 | 86 | if opts != OptionData { 87 | continue 88 | } 89 | 90 | _, err = io.ReadFull(m.Conn, m.length[:]) 91 | if err != nil { 92 | return 0, err 93 | } 94 | dataLen := int(binary.BigEndian.Uint16(m.length[:])) 95 | m.remain = dataLen 96 | if dataLen > len(b) { 97 | dataLen = len(b) 98 | } 99 | 100 | n, err := m.Conn.Read(b[:dataLen]) 101 | m.remain -= n 102 | return n, err 103 | } 104 | } 105 | 106 | func (m *Mux) Write(b []byte) (int, error) { 107 | if m.otb != nil { 108 | // create a sub connection 109 | if _, err := m.Conn.Write(m.otb); err != nil { 110 | return 0, err 111 | } 112 | m.otb = nil 113 | } 114 | m.buf.Reset() 115 | binary.Write(&m.buf, binary.BigEndian, uint16(4)) 116 | m.buf.Write(m.id[:]) 117 | m.buf.WriteByte(SessionStatusKeep) 118 | m.buf.WriteByte(OptionData) 119 | binary.Write(&m.buf, binary.BigEndian, uint16(len(b))) 120 | m.buf.Write(b) 121 | 122 | return m.Conn.Write(m.buf.Bytes()) 123 | } 124 | 125 | func (m *Mux) Close() error { 126 | _, err := m.Conn.Write([]byte{0x0, 0x4, m.id[0], m.id[1], SessionStatusEnd, OptionNone}) 127 | if err != nil { 128 | return err 129 | } 130 | return m.Conn.Close() 131 | } 132 | 133 | func NewMux(conn net.Conn, option MuxOption) *Mux { 134 | buf := &bytes.Buffer{} 135 | 136 | // fill empty length 137 | buf.Write([]byte{0x0, 0x0}) 138 | buf.Write(option.ID[:]) 139 | buf.WriteByte(SessionStatusNew) 140 | buf.WriteByte(OptionNone) 141 | 142 | // tcp 143 | netType := byte(0x1) 144 | if option.Type == "udp" { 145 | netType = byte(0x2) 146 | } 147 | buf.WriteByte(netType) 148 | 149 | // port 150 | binary.Write(buf, binary.BigEndian, option.Port) 151 | 152 | // address 153 | ip := net.ParseIP(option.Host) 154 | if ip == nil { 155 | buf.WriteByte(0x2) 156 | buf.WriteString(option.Host) 157 | } else if ipv4 := ip.To4(); ipv4 != nil { 158 | buf.WriteByte(0x1) 159 | buf.Write(ipv4) 160 | } else { 161 | buf.WriteByte(0x3) 162 | buf.Write(ip.To16()) 163 | } 164 | 165 | metadata := buf.Bytes() 166 | binary.BigEndian.PutUint16(metadata[:2], uint16(len(metadata)-2)) 167 | 168 | return &Mux{ 169 | Conn: conn, 170 | id: option.ID, 171 | otb: metadata, 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /component/v2ray-plugin/websocket.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "crypto/tls" 5 | "net" 6 | "net/http" 7 | 8 | "github.com/zu1k/clashr/component/vmess" 9 | ) 10 | 11 | // Option is options of websocket obfs 12 | type Option struct { 13 | Host string 14 | Path string 15 | Headers map[string]string 16 | TLSConfig *tls.Config 17 | Mux bool 18 | } 19 | 20 | // NewV2rayObfs return a HTTPObfs 21 | func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) { 22 | header := http.Header{} 23 | for k, v := range option.Headers { 24 | header.Add(k, v) 25 | } 26 | 27 | config := &vmess.WebsocketConfig{ 28 | Host: option.Host, 29 | Path: option.Path, 30 | TLS: option.TLSConfig != nil, 31 | Headers: header, 32 | TLSConfig: option.TLSConfig, 33 | } 34 | 35 | var err error 36 | conn, err = vmess.NewWebsocketConn(conn, config) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | if option.Mux { 42 | conn = NewMux(conn, MuxOption{ 43 | ID: [2]byte{0, 0}, 44 | Host: "127.0.0.1", 45 | Port: 0, 46 | }) 47 | } 48 | return conn, nil 49 | } 50 | -------------------------------------------------------------------------------- /component/vmess/aead.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "crypto/cipher" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | 9 | "github.com/zu1k/clashr/common/pool" 10 | ) 11 | 12 | type aeadWriter struct { 13 | io.Writer 14 | cipher.AEAD 15 | nonce [32]byte 16 | count uint16 17 | iv []byte 18 | } 19 | 20 | func newAEADWriter(w io.Writer, aead cipher.AEAD, iv []byte) *aeadWriter { 21 | return &aeadWriter{Writer: w, AEAD: aead, iv: iv} 22 | } 23 | 24 | func (w *aeadWriter) Write(b []byte) (n int, err error) { 25 | buf := pool.BufPool.Get().([]byte) 26 | defer pool.BufPool.Put(buf[:cap(buf)]) 27 | length := len(b) 28 | for { 29 | if length == 0 { 30 | break 31 | } 32 | readLen := chunkSize - w.Overhead() 33 | if length < readLen { 34 | readLen = length 35 | } 36 | payloadBuf := buf[lenSize : lenSize+chunkSize-w.Overhead()] 37 | copy(payloadBuf, b[n:n+readLen]) 38 | 39 | binary.BigEndian.PutUint16(buf[:lenSize], uint16(readLen+w.Overhead())) 40 | binary.BigEndian.PutUint16(w.nonce[:2], w.count) 41 | copy(w.nonce[2:], w.iv[2:12]) 42 | 43 | w.Seal(payloadBuf[:0], w.nonce[:w.NonceSize()], payloadBuf[:readLen], nil) 44 | w.count++ 45 | 46 | _, err = w.Writer.Write(buf[:lenSize+readLen+w.Overhead()]) 47 | if err != nil { 48 | break 49 | } 50 | n += readLen 51 | length -= readLen 52 | } 53 | return 54 | } 55 | 56 | type aeadReader struct { 57 | io.Reader 58 | cipher.AEAD 59 | nonce [32]byte 60 | buf []byte 61 | offset int 62 | iv []byte 63 | sizeBuf []byte 64 | count uint16 65 | } 66 | 67 | func newAEADReader(r io.Reader, aead cipher.AEAD, iv []byte) *aeadReader { 68 | return &aeadReader{Reader: r, AEAD: aead, iv: iv, sizeBuf: make([]byte, lenSize)} 69 | } 70 | 71 | func (r *aeadReader) Read(b []byte) (int, error) { 72 | if r.buf != nil { 73 | n := copy(b, r.buf[r.offset:]) 74 | r.offset += n 75 | if r.offset == len(r.buf) { 76 | pool.BufPool.Put(r.buf[:cap(r.buf)]) 77 | r.buf = nil 78 | } 79 | return n, nil 80 | } 81 | 82 | _, err := io.ReadFull(r.Reader, r.sizeBuf) 83 | if err != nil { 84 | return 0, err 85 | } 86 | 87 | size := int(binary.BigEndian.Uint16(r.sizeBuf)) 88 | if size > maxSize { 89 | return 0, errors.New("Buffer is larger than standard") 90 | } 91 | 92 | buf := pool.BufPool.Get().([]byte) 93 | _, err = io.ReadFull(r.Reader, buf[:size]) 94 | if err != nil { 95 | pool.BufPool.Put(buf[:cap(buf)]) 96 | return 0, err 97 | } 98 | 99 | binary.BigEndian.PutUint16(r.nonce[:2], r.count) 100 | copy(r.nonce[2:], r.iv[2:12]) 101 | 102 | _, err = r.Open(buf[:0], r.nonce[:r.NonceSize()], buf[:size], nil) 103 | r.count++ 104 | if err != nil { 105 | return 0, err 106 | } 107 | realLen := size - r.Overhead() 108 | n := copy(b, buf[:realLen]) 109 | if len(b) >= realLen { 110 | pool.BufPool.Put(buf[:cap(buf)]) 111 | return n, nil 112 | } 113 | 114 | r.offset = n 115 | r.buf = buf[:realLen] 116 | return n, nil 117 | } 118 | -------------------------------------------------------------------------------- /component/vmess/chunk.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | 8 | "github.com/zu1k/clashr/common/pool" 9 | ) 10 | 11 | const ( 12 | lenSize = 2 13 | chunkSize = 1 << 14 // 2 ** 14 == 16 * 1024 14 | maxSize = 17 * 1024 // 2 + chunkSize + aead.Overhead() 15 | ) 16 | 17 | type chunkReader struct { 18 | io.Reader 19 | buf []byte 20 | sizeBuf []byte 21 | offset int 22 | } 23 | 24 | func newChunkReader(reader io.Reader) *chunkReader { 25 | return &chunkReader{Reader: reader, sizeBuf: make([]byte, lenSize)} 26 | } 27 | 28 | func newChunkWriter(writer io.WriteCloser) *chunkWriter { 29 | return &chunkWriter{Writer: writer} 30 | } 31 | 32 | func (cr *chunkReader) Read(b []byte) (int, error) { 33 | if cr.buf != nil { 34 | n := copy(b, cr.buf[cr.offset:]) 35 | cr.offset += n 36 | if cr.offset == len(cr.buf) { 37 | pool.BufPool.Put(cr.buf[:cap(cr.buf)]) 38 | cr.buf = nil 39 | } 40 | return n, nil 41 | } 42 | 43 | _, err := io.ReadFull(cr.Reader, cr.sizeBuf) 44 | if err != nil { 45 | return 0, err 46 | } 47 | 48 | size := int(binary.BigEndian.Uint16(cr.sizeBuf)) 49 | if size > maxSize { 50 | return 0, errors.New("Buffer is larger than standard") 51 | } 52 | 53 | if len(b) >= size { 54 | _, err := io.ReadFull(cr.Reader, b[:size]) 55 | if err != nil { 56 | return 0, err 57 | } 58 | 59 | return size, nil 60 | } 61 | 62 | buf := pool.BufPool.Get().([]byte) 63 | _, err = io.ReadFull(cr.Reader, buf[:size]) 64 | if err != nil { 65 | pool.BufPool.Put(buf[:cap(buf)]) 66 | return 0, err 67 | } 68 | n := copy(b, cr.buf[:]) 69 | cr.offset = n 70 | cr.buf = buf[:size] 71 | return n, nil 72 | } 73 | 74 | type chunkWriter struct { 75 | io.Writer 76 | } 77 | 78 | func (cw *chunkWriter) Write(b []byte) (n int, err error) { 79 | buf := pool.BufPool.Get().([]byte) 80 | defer pool.BufPool.Put(buf[:cap(buf)]) 81 | length := len(b) 82 | for { 83 | if length == 0 { 84 | break 85 | } 86 | readLen := chunkSize 87 | if length < chunkSize { 88 | readLen = length 89 | } 90 | payloadBuf := buf[lenSize : lenSize+chunkSize] 91 | copy(payloadBuf, b[n:n+readLen]) 92 | 93 | binary.BigEndian.PutUint16(buf[:lenSize], uint16(readLen)) 94 | _, err = cw.Writer.Write(buf[:lenSize+readLen]) 95 | if err != nil { 96 | break 97 | } 98 | n += readLen 99 | length -= readLen 100 | } 101 | return 102 | } 103 | -------------------------------------------------------------------------------- /component/vmess/conn.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/hmac" 8 | "crypto/md5" 9 | "encoding/binary" 10 | "errors" 11 | "hash/fnv" 12 | "io" 13 | "math/rand" 14 | "net" 15 | "time" 16 | 17 | "golang.org/x/crypto/chacha20poly1305" 18 | ) 19 | 20 | func init() { 21 | rand.Seed(time.Now().UnixNano()) 22 | } 23 | 24 | // Conn wrapper a net.Conn with vmess protocol 25 | type Conn struct { 26 | net.Conn 27 | reader io.Reader 28 | writer io.Writer 29 | dst *DstAddr 30 | id *ID 31 | reqBodyIV []byte 32 | reqBodyKey []byte 33 | respBodyIV []byte 34 | respBodyKey []byte 35 | respV byte 36 | security byte 37 | 38 | received bool 39 | } 40 | 41 | func (vc *Conn) Write(b []byte) (int, error) { 42 | return vc.writer.Write(b) 43 | } 44 | 45 | func (vc *Conn) Read(b []byte) (int, error) { 46 | if vc.received { 47 | return vc.reader.Read(b) 48 | } 49 | 50 | if err := vc.recvResponse(); err != nil { 51 | return 0, err 52 | } 53 | vc.received = true 54 | return vc.reader.Read(b) 55 | } 56 | 57 | func (vc *Conn) sendRequest() error { 58 | timestamp := time.Now() 59 | 60 | h := hmac.New(md5.New, vc.id.UUID.Bytes()) 61 | binary.Write(h, binary.BigEndian, uint64(timestamp.Unix())) 62 | _, err := vc.Conn.Write(h.Sum(nil)) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | buf := &bytes.Buffer{} 68 | 69 | // Ver IV Key V Opt 70 | buf.WriteByte(Version) 71 | buf.Write(vc.reqBodyIV[:]) 72 | buf.Write(vc.reqBodyKey[:]) 73 | buf.WriteByte(vc.respV) 74 | buf.WriteByte(OptionChunkStream) 75 | 76 | p := rand.Intn(16) 77 | // P Sec Reserve Cmd 78 | buf.WriteByte(byte(p<<4) | byte(vc.security)) 79 | buf.WriteByte(0) 80 | if vc.dst.UDP { 81 | buf.WriteByte(CommandUDP) 82 | } else { 83 | buf.WriteByte(CommandTCP) 84 | } 85 | 86 | // Port AddrType Addr 87 | binary.Write(buf, binary.BigEndian, uint16(vc.dst.Port)) 88 | buf.WriteByte(vc.dst.AddrType) 89 | buf.Write(vc.dst.Addr) 90 | 91 | // padding 92 | if p > 0 { 93 | padding := make([]byte, p) 94 | rand.Read(padding) 95 | buf.Write(padding) 96 | } 97 | 98 | fnv1a := fnv.New32a() 99 | fnv1a.Write(buf.Bytes()) 100 | buf.Write(fnv1a.Sum(nil)) 101 | 102 | block, err := aes.NewCipher(vc.id.CmdKey) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp)) 108 | stream.XORKeyStream(buf.Bytes(), buf.Bytes()) 109 | _, err = vc.Conn.Write(buf.Bytes()) 110 | return err 111 | } 112 | 113 | func (vc *Conn) recvResponse() error { 114 | block, err := aes.NewCipher(vc.respBodyKey[:]) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | stream := cipher.NewCFBDecrypter(block, vc.respBodyIV[:]) 120 | buf := make([]byte, 4) 121 | _, err = io.ReadFull(vc.Conn, buf) 122 | if err != nil { 123 | return err 124 | } 125 | stream.XORKeyStream(buf, buf) 126 | 127 | if buf[0] != vc.respV { 128 | return errors.New("unexpected response header") 129 | } 130 | 131 | if buf[2] != 0 { 132 | return errors.New("dynamic port is not supported now") 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func hashTimestamp(t time.Time) []byte { 139 | md5hash := md5.New() 140 | ts := make([]byte, 8) 141 | binary.BigEndian.PutUint64(ts, uint64(t.Unix())) 142 | md5hash.Write(ts) 143 | md5hash.Write(ts) 144 | md5hash.Write(ts) 145 | md5hash.Write(ts) 146 | return md5hash.Sum(nil) 147 | } 148 | 149 | // newConn return a Conn instance 150 | func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security) (*Conn, error) { 151 | randBytes := make([]byte, 33) 152 | rand.Read(randBytes) 153 | reqBodyIV := make([]byte, 16) 154 | reqBodyKey := make([]byte, 16) 155 | copy(reqBodyIV[:], randBytes[:16]) 156 | copy(reqBodyKey[:], randBytes[16:32]) 157 | respV := randBytes[32] 158 | 159 | respBodyKey := md5.Sum(reqBodyKey[:]) 160 | respBodyIV := md5.Sum(reqBodyIV[:]) 161 | 162 | var writer io.Writer 163 | var reader io.Reader 164 | switch security { 165 | case SecurityNone: 166 | reader = newChunkReader(conn) 167 | writer = newChunkWriter(conn) 168 | case SecurityAES128GCM: 169 | block, _ := aes.NewCipher(reqBodyKey[:]) 170 | aead, _ := cipher.NewGCM(block) 171 | writer = newAEADWriter(conn, aead, reqBodyIV[:]) 172 | 173 | block, _ = aes.NewCipher(respBodyKey[:]) 174 | aead, _ = cipher.NewGCM(block) 175 | reader = newAEADReader(conn, aead, respBodyIV[:]) 176 | case SecurityCHACHA20POLY1305: 177 | key := make([]byte, 32) 178 | t := md5.Sum(reqBodyKey[:]) 179 | copy(key, t[:]) 180 | t = md5.Sum(key[:16]) 181 | copy(key[16:], t[:]) 182 | aead, _ := chacha20poly1305.New(key) 183 | writer = newAEADWriter(conn, aead, reqBodyIV[:]) 184 | 185 | t = md5.Sum(respBodyKey[:]) 186 | copy(key, t[:]) 187 | t = md5.Sum(key[:16]) 188 | copy(key[16:], t[:]) 189 | aead, _ = chacha20poly1305.New(key) 190 | reader = newAEADReader(conn, aead, respBodyIV[:]) 191 | } 192 | 193 | c := &Conn{ 194 | Conn: conn, 195 | id: id, 196 | dst: dst, 197 | reqBodyIV: reqBodyIV, 198 | reqBodyKey: reqBodyKey, 199 | respV: respV, 200 | respBodyIV: respBodyIV[:], 201 | respBodyKey: respBodyKey[:], 202 | reader: reader, 203 | writer: writer, 204 | security: security, 205 | } 206 | if err := c.sendRequest(); err != nil { 207 | return nil, err 208 | } 209 | return c, nil 210 | } 211 | -------------------------------------------------------------------------------- /component/vmess/user.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | 7 | "github.com/gofrs/uuid" 8 | ) 9 | 10 | // ID cmdKey length 11 | const ( 12 | IDBytesLen = 16 13 | ) 14 | 15 | // The ID of en entity, in the form of a UUID. 16 | type ID struct { 17 | UUID *uuid.UUID 18 | CmdKey []byte 19 | } 20 | 21 | // newID returns an ID with given UUID. 22 | func newID(uuid *uuid.UUID) *ID { 23 | id := &ID{UUID: uuid, CmdKey: make([]byte, IDBytesLen)} 24 | md5hash := md5.New() 25 | md5hash.Write(uuid.Bytes()) 26 | md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21")) 27 | md5hash.Sum(id.CmdKey[:0]) 28 | return id 29 | } 30 | 31 | func nextID(u *uuid.UUID) *uuid.UUID { 32 | md5hash := md5.New() 33 | md5hash.Write(u.Bytes()) 34 | md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) 35 | var newid uuid.UUID 36 | for { 37 | md5hash.Sum(newid[:0]) 38 | if !bytes.Equal(newid.Bytes(), u.Bytes()) { 39 | return &newid 40 | } 41 | md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")) 42 | } 43 | } 44 | 45 | func newAlterIDs(primary *ID, alterIDCount uint16) []*ID { 46 | alterIDs := make([]*ID, alterIDCount) 47 | prevID := primary.UUID 48 | for idx := range alterIDs { 49 | newid := nextID(prevID) 50 | alterIDs[idx] = &ID{UUID: newid, CmdKey: primary.CmdKey[:]} 51 | prevID = newid 52 | } 53 | alterIDs = append(alterIDs, primary) 54 | return alterIDs 55 | } 56 | -------------------------------------------------------------------------------- /component/vmess/vmess.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "math/rand" 7 | "net" 8 | "net/http" 9 | "runtime" 10 | "sync" 11 | 12 | "github.com/gofrs/uuid" 13 | ) 14 | 15 | // Version of vmess 16 | const Version byte = 1 17 | 18 | // Request Options 19 | const ( 20 | OptionChunkStream byte = 1 21 | OptionChunkMasking byte = 4 22 | ) 23 | 24 | // Security type vmess 25 | type Security = byte 26 | 27 | // Cipher types 28 | const ( 29 | SecurityAES128GCM Security = 3 30 | SecurityCHACHA20POLY1305 Security = 4 31 | SecurityNone Security = 5 32 | ) 33 | 34 | // CipherMapping return 35 | var CipherMapping = map[string]byte{ 36 | "none": SecurityNone, 37 | "aes-128-gcm": SecurityAES128GCM, 38 | "chacha20-poly1305": SecurityCHACHA20POLY1305, 39 | } 40 | 41 | var ( 42 | clientSessionCache tls.ClientSessionCache 43 | once sync.Once 44 | ) 45 | 46 | // Command types 47 | const ( 48 | CommandTCP byte = 1 49 | CommandUDP byte = 2 50 | ) 51 | 52 | // Addr types 53 | const ( 54 | AtypIPv4 byte = 1 55 | AtypDomainName byte = 2 56 | AtypIPv6 byte = 3 57 | ) 58 | 59 | // DstAddr store destination address 60 | type DstAddr struct { 61 | UDP bool 62 | AddrType byte 63 | Addr []byte 64 | Port uint 65 | } 66 | 67 | // Client is vmess connection generator 68 | type Client struct { 69 | user []*ID 70 | uuid *uuid.UUID 71 | security Security 72 | tls bool 73 | host string 74 | wsConfig *WebsocketConfig 75 | tlsConfig *tls.Config 76 | } 77 | 78 | // Config of vmess 79 | type Config struct { 80 | UUID string 81 | AlterID uint16 82 | Security string 83 | TLS bool 84 | HostName string 85 | Port string 86 | NetWork string 87 | WebSocketPath string 88 | WebSocketHeaders map[string]string 89 | SkipCertVerify bool 90 | SessionCache tls.ClientSessionCache 91 | } 92 | 93 | // New return a Conn with net.Conn and DstAddr 94 | func (c *Client) New(conn net.Conn, dst *DstAddr) (net.Conn, error) { 95 | var err error 96 | r := rand.Intn(len(c.user)) 97 | if c.wsConfig != nil { 98 | conn, err = NewWebsocketConn(conn, c.wsConfig) 99 | if err != nil { 100 | return nil, err 101 | } 102 | } else if c.tls { 103 | conn = tls.Client(conn, c.tlsConfig) 104 | } 105 | return newConn(conn, c.user[r], dst, c.security) 106 | } 107 | 108 | // NewClient return Client instance 109 | func NewClient(config Config) (*Client, error) { 110 | uid, err := uuid.FromString(config.UUID) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | var security Security 116 | switch config.Security { 117 | case "aes-128-gcm": 118 | security = SecurityAES128GCM 119 | case "chacha20-poly1305": 120 | security = SecurityCHACHA20POLY1305 121 | case "none": 122 | security = SecurityNone 123 | case "auto": 124 | security = SecurityCHACHA20POLY1305 125 | if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" { 126 | security = SecurityAES128GCM 127 | } 128 | default: 129 | return nil, fmt.Errorf("Unknown security type: %s", config.Security) 130 | } 131 | 132 | if config.NetWork != "" && config.NetWork != "ws" { 133 | return nil, fmt.Errorf("Unknown network type: %s", config.NetWork) 134 | } 135 | 136 | header := http.Header{} 137 | for k, v := range config.WebSocketHeaders { 138 | header.Add(k, v) 139 | } 140 | 141 | host := net.JoinHostPort(config.HostName, config.Port) 142 | 143 | var tlsConfig *tls.Config 144 | if config.TLS { 145 | tlsConfig = &tls.Config{ 146 | ServerName: config.HostName, 147 | InsecureSkipVerify: config.SkipCertVerify, 148 | ClientSessionCache: config.SessionCache, 149 | } 150 | if tlsConfig.ClientSessionCache == nil { 151 | tlsConfig.ClientSessionCache = getClientSessionCache() 152 | } 153 | if host := header.Get("Host"); host != "" { 154 | tlsConfig.ServerName = host 155 | } 156 | } 157 | 158 | var wsConfig *WebsocketConfig 159 | if config.NetWork == "ws" { 160 | wsConfig = &WebsocketConfig{ 161 | Host: host, 162 | Path: config.WebSocketPath, 163 | Headers: header, 164 | TLS: config.TLS, 165 | TLSConfig: tlsConfig, 166 | } 167 | } 168 | 169 | return &Client{ 170 | user: newAlterIDs(newID(&uid), config.AlterID), 171 | uuid: &uid, 172 | security: security, 173 | tls: config.TLS, 174 | host: host, 175 | wsConfig: wsConfig, 176 | tlsConfig: tlsConfig, 177 | }, nil 178 | } 179 | 180 | func getClientSessionCache() tls.ClientSessionCache { 181 | once.Do(func() { 182 | clientSessionCache = tls.NewLRUClientSessionCache(128) 183 | }) 184 | return clientSessionCache 185 | } 186 | -------------------------------------------------------------------------------- /component/vmess/websocket.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "io" 7 | "net" 8 | "net/http" 9 | "net/url" 10 | "strings" 11 | "time" 12 | 13 | "github.com/gorilla/websocket" 14 | ) 15 | 16 | type websocketConn struct { 17 | conn *websocket.Conn 18 | reader io.Reader 19 | remoteAddr net.Addr 20 | } 21 | 22 | type WebsocketConfig struct { 23 | Host string 24 | Path string 25 | Headers http.Header 26 | TLS bool 27 | TLSConfig *tls.Config 28 | } 29 | 30 | // Read implements net.Conn.Read() 31 | func (wsc *websocketConn) Read(b []byte) (int, error) { 32 | for { 33 | reader, err := wsc.getReader() 34 | if err != nil { 35 | return 0, err 36 | } 37 | 38 | nBytes, err := reader.Read(b) 39 | if err == io.EOF { 40 | wsc.reader = nil 41 | continue 42 | } 43 | return nBytes, err 44 | } 45 | } 46 | 47 | // Write implements io.Writer. 48 | func (wsc *websocketConn) Write(b []byte) (int, error) { 49 | if err := wsc.conn.WriteMessage(websocket.BinaryMessage, b); err != nil { 50 | return 0, err 51 | } 52 | return len(b), nil 53 | } 54 | 55 | func (wsc *websocketConn) Close() error { 56 | var errors []string 57 | if err := wsc.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)); err != nil { 58 | errors = append(errors, err.Error()) 59 | } 60 | if err := wsc.conn.Close(); err != nil { 61 | errors = append(errors, err.Error()) 62 | } 63 | if len(errors) > 0 { 64 | return fmt.Errorf("Failed to close connection: %s", strings.Join(errors, ",")) 65 | } 66 | return nil 67 | } 68 | 69 | func (wsc *websocketConn) getReader() (io.Reader, error) { 70 | if wsc.reader != nil { 71 | return wsc.reader, nil 72 | } 73 | 74 | _, reader, err := wsc.conn.NextReader() 75 | if err != nil { 76 | return nil, err 77 | } 78 | wsc.reader = reader 79 | return reader, nil 80 | } 81 | 82 | func (wsc *websocketConn) LocalAddr() net.Addr { 83 | return wsc.conn.LocalAddr() 84 | } 85 | 86 | func (wsc *websocketConn) RemoteAddr() net.Addr { 87 | return wsc.remoteAddr 88 | } 89 | 90 | func (wsc *websocketConn) SetDeadline(t time.Time) error { 91 | if err := wsc.SetReadDeadline(t); err != nil { 92 | return err 93 | } 94 | return wsc.SetWriteDeadline(t) 95 | } 96 | 97 | func (wsc *websocketConn) SetReadDeadline(t time.Time) error { 98 | return wsc.conn.SetReadDeadline(t) 99 | } 100 | 101 | func (wsc *websocketConn) SetWriteDeadline(t time.Time) error { 102 | return wsc.conn.SetWriteDeadline(t) 103 | } 104 | 105 | func NewWebsocketConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) { 106 | dialer := &websocket.Dialer{ 107 | NetDial: func(network, addr string) (net.Conn, error) { 108 | return conn, nil 109 | }, 110 | ReadBufferSize: 4 * 1024, 111 | WriteBufferSize: 4 * 1024, 112 | HandshakeTimeout: time.Second * 8, 113 | } 114 | 115 | scheme := "ws" 116 | if c.TLS { 117 | scheme = "wss" 118 | dialer.TLSClientConfig = c.TLSConfig 119 | } 120 | 121 | host, port, _ := net.SplitHostPort(c.Host) 122 | if (scheme == "ws" && port != "80") || (scheme == "wss" && port != "443") { 123 | host = c.Host 124 | } 125 | 126 | uri := url.URL{ 127 | Scheme: scheme, 128 | Host: host, 129 | Path: c.Path, 130 | } 131 | 132 | headers := http.Header{} 133 | if c.Headers != nil { 134 | for k := range c.Headers { 135 | headers.Add(k, c.Headers.Get(k)) 136 | } 137 | } 138 | 139 | wsConn, resp, err := dialer.Dial(uri.String(), headers) 140 | if err != nil { 141 | reason := err.Error() 142 | if resp != nil { 143 | reason = resp.Status 144 | } 145 | return nil, fmt.Errorf("Dial %s error: %s", host, reason) 146 | } 147 | 148 | return &websocketConn{ 149 | conn: wsConn, 150 | remoteAddr: conn.RemoteAddr(), 151 | }, nil 152 | } 153 | -------------------------------------------------------------------------------- /config/initial.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "archive/tar" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "strings" 11 | 12 | C "github.com/zu1k/clashr/constant" 13 | 14 | log "github.com/sirupsen/logrus" 15 | ) 16 | 17 | func downloadMMDB(path string) (err error) { 18 | resp, err := http.Get("http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz") 19 | if err != nil { 20 | return 21 | } 22 | defer resp.Body.Close() 23 | 24 | gr, err := gzip.NewReader(resp.Body) 25 | if err != nil { 26 | return 27 | } 28 | defer gr.Close() 29 | 30 | tr := tar.NewReader(gr) 31 | for { 32 | h, err := tr.Next() 33 | if err == io.EOF { 34 | break 35 | } else if err != nil { 36 | return err 37 | } 38 | 39 | if !strings.HasSuffix(h.Name, "GeoLite2-Country.mmdb") { 40 | continue 41 | } 42 | 43 | f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) 44 | if err != nil { 45 | return err 46 | } 47 | defer f.Close() 48 | _, err = io.Copy(f, tr) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // Init prepare necessary files 58 | func Init(dir string) error { 59 | // initial homedir 60 | if _, err := os.Stat(dir); os.IsNotExist(err) { 61 | if err := os.MkdirAll(dir, 0777); err != nil { 62 | return fmt.Errorf("Can't create config directory %s: %s", dir, err.Error()) 63 | } 64 | } 65 | 66 | // initial config.yaml 67 | if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) { 68 | log.Info("Can't find config, create an empty file") 69 | os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0644) 70 | } 71 | 72 | // initial mmdb 73 | if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) { 74 | log.Info("Can't find MMDB, start download") 75 | err := downloadMMDB(C.Path.MMDB()) 76 | if err != nil { 77 | return fmt.Errorf("Can't download MMDB: %s", err.Error()) 78 | } 79 | } 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /config/utils.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | C "github.com/zu1k/clashr/constant" 8 | adapters "github.com/zu1k/clashr/adapters/outbound" 9 | "github.com/zu1k/clashr/common/structure" 10 | ) 11 | 12 | func trimArr(arr []string) (r []string) { 13 | for _, e := range arr { 14 | r = append(r, strings.Trim(e, " ")) 15 | } 16 | return 17 | } 18 | 19 | func getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) { 20 | var ps []C.Proxy 21 | for _, name := range list { 22 | p, ok := mapping[name] 23 | if !ok { 24 | return nil, fmt.Errorf("'%s' not found", name) 25 | } 26 | ps = append(ps, p) 27 | } 28 | return ps, nil 29 | } 30 | 31 | func or(pointers ...*int) *int { 32 | for _, p := range pointers { 33 | if p != nil { 34 | return p 35 | } 36 | } 37 | return pointers[len(pointers)-1] 38 | } 39 | 40 | // Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order. 41 | // Meanwhile, record the original index in the config file. 42 | // If loop is detected, return an error with location of loop. 43 | func proxyGroupsDagSort(groupsConfig []map[string]interface{}, decoder *structure.Decoder) error { 44 | 45 | type graphNode struct { 46 | indegree int 47 | // topological order 48 | topo int 49 | // the origional data in `groupsConfig` 50 | data map[string]interface{} 51 | // `outdegree` and `from` are used in loop locating 52 | outdegree int 53 | from []string 54 | } 55 | 56 | graph := make(map[string]*graphNode) 57 | 58 | // Step 1.1 build dependency graph 59 | for _, mapping := range groupsConfig { 60 | option := &adapters.ProxyGroupOption{} 61 | err := decoder.Decode(mapping, option) 62 | groupName := option.Name 63 | if err != nil { 64 | return fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error()) 65 | } 66 | if node, ok := graph[groupName]; ok { 67 | if node.data != nil { 68 | return fmt.Errorf("ProxyGroup %s: duplicate group name", groupName) 69 | } 70 | node.data = mapping 71 | } else { 72 | graph[groupName] = &graphNode{0, -1, mapping, 0, nil} 73 | } 74 | 75 | for _, proxy := range option.Proxies { 76 | if node, ex := graph[proxy]; ex { 77 | node.indegree++ 78 | } else { 79 | graph[proxy] = &graphNode{1, -1, nil, 0, nil} 80 | } 81 | } 82 | } 83 | // Step 1.2 Topological Sort 84 | // topological index of **ProxyGroup** 85 | index := 0 86 | queue := make([]string, 0) 87 | for name, node := range graph { 88 | // in the begning, put nodes that have `node.indegree == 0` into queue. 89 | if node.indegree == 0 { 90 | queue = append(queue, name) 91 | } 92 | } 93 | // every element in queue have indegree == 0 94 | for ; len(queue) > 0; queue = queue[1:] { 95 | name := queue[0] 96 | node := graph[name] 97 | if node.data != nil { 98 | index++ 99 | groupsConfig[len(groupsConfig)-index] = node.data 100 | for _, proxy := range node.data["proxies"].([]interface{}) { 101 | child := graph[proxy.(string)] 102 | child.indegree-- 103 | if child.indegree == 0 { 104 | queue = append(queue, proxy.(string)) 105 | } 106 | } 107 | } 108 | delete(graph, name) 109 | } 110 | 111 | // no loop is detected, return sorted ProxyGroup 112 | if len(graph) == 0 { 113 | return nil 114 | } 115 | 116 | // if loop is detected, locate the loop and throw an error 117 | // Step 2.1 rebuild the graph, fill `outdegree` and `from` filed 118 | for name, node := range graph { 119 | if node.data == nil { 120 | continue 121 | } 122 | for _, proxy := range node.data["proxies"].([]interface{}) { 123 | node.outdegree++ 124 | child := graph[proxy.(string)] 125 | if child.from == nil { 126 | child.from = make([]string, 0, child.indegree) 127 | } 128 | child.from = append(child.from, name) 129 | } 130 | } 131 | // Step 2.2 remove nodes outside the loop. so that we have only the loops remain in `graph` 132 | queue = make([]string, 0) 133 | // initialize queue with node have outdegree == 0 134 | for name, node := range graph { 135 | if node.outdegree == 0 { 136 | queue = append(queue, name) 137 | } 138 | } 139 | // every element in queue have outdegree == 0 140 | for ; len(queue) > 0; queue = queue[1:] { 141 | name := queue[0] 142 | node := graph[name] 143 | for _, f := range node.from { 144 | graph[f].outdegree-- 145 | if graph[f].outdegree == 0 { 146 | queue = append(queue, f) 147 | } 148 | } 149 | delete(graph, name) 150 | } 151 | // Step 2.3 report the elements in loop 152 | loopElements := make([]string, 0, len(graph)) 153 | for name := range graph { 154 | loopElements = append(loopElements, name) 155 | delete(graph, name) 156 | } 157 | return fmt.Errorf("Loop is detected in ProxyGroup, please check following ProxyGroups: %v", loopElements) 158 | } 159 | -------------------------------------------------------------------------------- /constant/adapters.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "time" 8 | ) 9 | 10 | // Adapter Type 11 | const ( 12 | Direct AdapterType = iota 13 | Fallback 14 | Reject 15 | Selector 16 | Shadowsocks 17 | ShadowsocksR 18 | Snell 19 | Socks5 20 | Http 21 | URLTest 22 | Vmess 23 | LoadBalance 24 | ) 25 | 26 | type ServerAdapter interface { 27 | net.Conn 28 | Metadata() *Metadata 29 | } 30 | 31 | type Connection interface { 32 | Chains() Chain 33 | AppendToChains(adapter ProxyAdapter) 34 | } 35 | 36 | type Chain []string 37 | 38 | func (c Chain) String() string { 39 | switch len(c) { 40 | case 0: 41 | return "" 42 | case 1: 43 | return c[0] 44 | default: 45 | return fmt.Sprintf("%s[%s]", c[len(c)-1], c[0]) 46 | } 47 | } 48 | 49 | type Conn interface { 50 | net.Conn 51 | Connection 52 | } 53 | 54 | type PacketConn interface { 55 | net.PacketConn 56 | Connection 57 | } 58 | 59 | type ProxyAdapter interface { 60 | Name() string 61 | Type() AdapterType 62 | DialContext(ctx context.Context, metadata *Metadata) (Conn, error) 63 | DialUDP(metadata *Metadata) (PacketConn, net.Addr, error) 64 | SupportUDP() bool 65 | Destroy() 66 | MarshalJSON() ([]byte, error) 67 | } 68 | 69 | type DelayHistory struct { 70 | Time time.Time `json:"time"` 71 | Delay uint16 `json:"delay"` 72 | } 73 | 74 | type Proxy interface { 75 | ProxyAdapter 76 | Alive() bool 77 | DelayHistory() []DelayHistory 78 | Dial(metadata *Metadata) (Conn, error) 79 | LastDelay() uint16 80 | URLTest(ctx context.Context, url string) (uint16, error) 81 | } 82 | 83 | // AdapterType is enum of adapter type 84 | type AdapterType int 85 | 86 | func (at AdapterType) String() string { 87 | switch at { 88 | case Direct: 89 | return "Direct" 90 | case Fallback: 91 | return "Fallback" 92 | case Reject: 93 | return "Reject" 94 | case Selector: 95 | return "Selector" 96 | case Shadowsocks: 97 | return "Shadowsocks" 98 | case ShadowsocksR: 99 | return "ShadowsocksR" 100 | case Snell: 101 | return "Snell" 102 | case Socks5: 103 | return "Socks5" 104 | case Http: 105 | return "Http" 106 | case URLTest: 107 | return "URLTest" 108 | case Vmess: 109 | return "Vmess" 110 | case LoadBalance: 111 | return "LoadBalance" 112 | default: 113 | return "Unknown" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /constant/metadata.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // Socks addr type 8 | const ( 9 | AtypIPv4 = 1 10 | AtypDomainName = 3 11 | AtypIPv6 = 4 12 | 13 | TCP NetWork = iota 14 | UDP 15 | 16 | HTTP Type = iota 17 | SOCKS 18 | REDIR 19 | ) 20 | 21 | type NetWork int 22 | 23 | func (n *NetWork) String() string { 24 | if *n == TCP { 25 | return "tcp" 26 | } 27 | return "udp" 28 | } 29 | 30 | type Type int 31 | 32 | // Metadata is used to store connection address 33 | type Metadata struct { 34 | NetWork NetWork 35 | Type Type 36 | SrcIP *net.IP 37 | DstIP *net.IP 38 | SrcPort string 39 | DstPort string 40 | AddrType int 41 | Host string 42 | } 43 | 44 | func (m *Metadata) RemoteAddress() string { 45 | return net.JoinHostPort(m.String(), m.DstPort) 46 | } 47 | 48 | func (m *Metadata) String() string { 49 | if m.Host != "" { 50 | return m.Host 51 | } else if m.DstIP != nil { 52 | return m.DstIP.String() 53 | } else { 54 | return "" 55 | } 56 | } 57 | 58 | func (m *Metadata) Valid() bool { 59 | return m.Host != "" || m.DstIP != nil 60 | } 61 | -------------------------------------------------------------------------------- /constant/path.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "os" 5 | P "path" 6 | ) 7 | 8 | const Name = "clash" 9 | 10 | // Path is used to get the configuration path 11 | var Path *path 12 | 13 | type path struct { 14 | homedir string 15 | } 16 | 17 | func init() { 18 | homedir, err := os.UserHomeDir() 19 | if err != nil { 20 | homedir, _ = os.Getwd() 21 | } 22 | 23 | homedir = P.Join(homedir, ".config", Name) 24 | Path = &path{homedir: homedir} 25 | } 26 | 27 | // SetHomeDir is used to set the configuration path 28 | func SetHomeDir(root string) { 29 | Path = &path{homedir: root} 30 | } 31 | 32 | func (p *path) HomeDir() string { 33 | return p.homedir 34 | } 35 | 36 | func (p *path) Config() string { 37 | return P.Join(p.homedir, "config.yaml") 38 | } 39 | 40 | func (p *path) MMDB() string { 41 | return P.Join(p.homedir, "Country.mmdb") 42 | } 43 | -------------------------------------------------------------------------------- /constant/rule.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | // Rule Type 4 | const ( 5 | Domain RuleType = iota 6 | DomainSuffix 7 | DomainKeyword 8 | GEOIP 9 | IPCIDR 10 | SrcIPCIDR 11 | SrcPort 12 | DstPort 13 | MATCH 14 | ) 15 | 16 | type RuleType int 17 | 18 | func (rt RuleType) String() string { 19 | switch rt { 20 | case Domain: 21 | return "Domain" 22 | case DomainSuffix: 23 | return "DomainSuffix" 24 | case DomainKeyword: 25 | return "DomainKeyword" 26 | case GEOIP: 27 | return "GEOIP" 28 | case IPCIDR: 29 | return "IPCIDR" 30 | case SrcIPCIDR: 31 | return "SrcIPCIDR" 32 | case SrcPort: 33 | return "SrcPort" 34 | case DstPort: 35 | return "DstPort" 36 | case MATCH: 37 | return "MATCH" 38 | default: 39 | return "Unknown" 40 | } 41 | } 42 | 43 | type Rule interface { 44 | RuleType() RuleType 45 | IsMatch(metadata *Metadata) bool 46 | Adapter() string 47 | Payload() string 48 | } 49 | -------------------------------------------------------------------------------- /constant/traffic.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Traffic struct { 8 | up chan int64 9 | down chan int64 10 | upCount int64 11 | downCount int64 12 | upTotal int64 13 | downTotal int64 14 | interval time.Duration 15 | } 16 | 17 | func (t *Traffic) Up() chan<- int64 { 18 | return t.up 19 | } 20 | 21 | func (t *Traffic) Down() chan<- int64 { 22 | return t.down 23 | } 24 | 25 | func (t *Traffic) Now() (up int64, down int64) { 26 | return t.upTotal, t.downTotal 27 | } 28 | 29 | func (t *Traffic) handle() { 30 | go t.handleCh(t.up, &t.upCount, &t.upTotal) 31 | go t.handleCh(t.down, &t.downCount, &t.downTotal) 32 | } 33 | 34 | func (t *Traffic) handleCh(ch <-chan int64, count *int64, total *int64) { 35 | ticker := time.NewTicker(t.interval) 36 | for { 37 | select { 38 | case n := <-ch: 39 | *count += n 40 | case <-ticker.C: 41 | *total = *count 42 | *count = 0 43 | } 44 | } 45 | } 46 | 47 | func NewTraffic(interval time.Duration) *Traffic { 48 | t := &Traffic{ 49 | up: make(chan int64), 50 | down: make(chan int64), 51 | interval: interval, 52 | } 53 | go t.handle() 54 | return t 55 | } 56 | -------------------------------------------------------------------------------- /constant/version.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | var ( 4 | Version = "unknown version" 5 | BuildTime = "unknown time" 6 | ) 7 | -------------------------------------------------------------------------------- /dns/client.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "context" 5 | 6 | D "github.com/miekg/dns" 7 | ) 8 | 9 | type client struct { 10 | *D.Client 11 | Address string 12 | } 13 | 14 | func (c *client) Exchange(m *D.Msg) (msg *D.Msg, err error) { 15 | return c.ExchangeContext(context.Background(), m) 16 | } 17 | 18 | func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { 19 | msg, _, err = c.Client.ExchangeContext(ctx, m, c.Address) 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /dns/doh.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/tls" 7 | "io/ioutil" 8 | "net/http" 9 | 10 | D "github.com/miekg/dns" 11 | ) 12 | 13 | const ( 14 | // dotMimeType is the DoH mimetype that should be used. 15 | dotMimeType = "application/dns-message" 16 | 17 | // dotPath is the URL path that should be used. 18 | dotPath = "/dns-query" 19 | ) 20 | 21 | var dohTransport = &http.Transport{ 22 | TLSClientConfig: &tls.Config{ClientSessionCache: globalSessionCache}, 23 | } 24 | 25 | type dohClient struct { 26 | url string 27 | } 28 | 29 | func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { 30 | return dc.ExchangeContext(context.Background(), m) 31 | } 32 | 33 | func (dc *dohClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { 34 | req, err := dc.newRequest(m) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | req = req.WithContext(ctx) 40 | return dc.doRequest(req) 41 | } 42 | 43 | // newRequest returns a new DoH request given a dns.Msg. 44 | func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) { 45 | buf, err := m.Pack() 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | req, err := http.NewRequest(http.MethodPost, dc.url+"?bla=foo:443", bytes.NewReader(buf)) 51 | if err != nil { 52 | return req, err 53 | } 54 | 55 | req.Header.Set("content-type", dotMimeType) 56 | req.Header.Set("accept", dotMimeType) 57 | return req, nil 58 | } 59 | 60 | func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { 61 | client := &http.Client{Transport: dohTransport} 62 | resp, err := client.Do(req) 63 | if err != nil { 64 | return nil, err 65 | } 66 | defer resp.Body.Close() 67 | 68 | buf, err := ioutil.ReadAll(resp.Body) 69 | if err != nil { 70 | return nil, err 71 | } 72 | msg = &D.Msg{} 73 | err = msg.Unpack(buf) 74 | return msg, err 75 | } 76 | -------------------------------------------------------------------------------- /dns/filters.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import "net" 4 | 5 | type fallbackFilter interface { 6 | Match(net.IP) bool 7 | } 8 | 9 | type geoipFilter struct{} 10 | 11 | func (gf *geoipFilter) Match(ip net.IP) bool { 12 | if mmdb == nil { 13 | return false 14 | } 15 | 16 | record, _ := mmdb.Country(ip) 17 | return record.Country.IsoCode == "CN" || record.Country.IsoCode == "" 18 | } 19 | 20 | type ipnetFilter struct { 21 | ipnet *net.IPNet 22 | } 23 | 24 | func (inf *ipnetFilter) Match(ip net.IP) bool { 25 | return inf.ipnet.Contains(ip) 26 | } 27 | -------------------------------------------------------------------------------- /dns/iputil.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | errIPNotFound = errors.New("cannot found ip") 11 | errIPVersion = errors.New("ip version error") 12 | ) 13 | 14 | // ResolveIPv4 with a host, return ipv4 15 | func ResolveIPv4(host string) (net.IP, error) { 16 | if node := DefaultHosts.Search(host); node != nil { 17 | if ip := node.Data.(net.IP).To4(); ip != nil { 18 | return ip, nil 19 | } 20 | } 21 | 22 | ip := net.ParseIP(host) 23 | if ip != nil { 24 | if !strings.Contains(host, ":") { 25 | return ip, nil 26 | } 27 | return nil, errIPVersion 28 | } 29 | 30 | if DefaultResolver != nil { 31 | return DefaultResolver.ResolveIPv4(host) 32 | } 33 | 34 | ipAddrs, err := net.LookupIP(host) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | for _, ip := range ipAddrs { 40 | if ip4 := ip.To4(); ip4 != nil { 41 | return ip4, nil 42 | } 43 | } 44 | 45 | return nil, errIPNotFound 46 | } 47 | 48 | // ResolveIPv6 with a host, return ipv6 49 | func ResolveIPv6(host string) (net.IP, error) { 50 | if node := DefaultHosts.Search(host); node != nil { 51 | if ip := node.Data.(net.IP).To16(); ip != nil { 52 | return ip, nil 53 | } 54 | } 55 | 56 | ip := net.ParseIP(host) 57 | if ip != nil { 58 | if strings.Contains(host, ":") { 59 | return ip, nil 60 | } 61 | return nil, errIPVersion 62 | } 63 | 64 | if DefaultResolver != nil { 65 | return DefaultResolver.ResolveIPv6(host) 66 | } 67 | 68 | ipAddrs, err := net.LookupIP(host) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | for _, ip := range ipAddrs { 74 | if ip.To4() == nil { 75 | return ip, nil 76 | } 77 | } 78 | 79 | return nil, errIPNotFound 80 | } 81 | 82 | // ResolveIP with a host, return ip 83 | func ResolveIP(host string) (net.IP, error) { 84 | if node := DefaultHosts.Search(host); node != nil { 85 | return node.Data.(net.IP), nil 86 | } 87 | 88 | if DefaultResolver != nil { 89 | if DefaultResolver.ipv6 { 90 | return DefaultResolver.ResolveIP(host) 91 | } 92 | return DefaultResolver.ResolveIPv4(host) 93 | } 94 | 95 | ip := net.ParseIP(host) 96 | if ip != nil { 97 | return ip, nil 98 | } 99 | 100 | ipAddr, err := net.ResolveIPAddr("ip", host) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | return ipAddr.IP, nil 106 | } 107 | -------------------------------------------------------------------------------- /dns/middleware.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/zu1k/clashr/component/fakeip" 7 | "github.com/zu1k/clashr/log" 8 | 9 | D "github.com/miekg/dns" 10 | ) 11 | 12 | type handler func(w D.ResponseWriter, r *D.Msg) 13 | type middleware func(next handler) handler 14 | 15 | func withFakeIP(fakePool *fakeip.Pool) middleware { 16 | return func(next handler) handler { 17 | return func(w D.ResponseWriter, r *D.Msg) { 18 | q := r.Question[0] 19 | 20 | if q.Qtype == D.TypeAAAA { 21 | D.HandleFailed(w, r) 22 | return 23 | } else if q.Qtype != D.TypeA { 24 | next(w, r) 25 | return 26 | } 27 | 28 | host := strings.TrimRight(q.Name, ".") 29 | 30 | rr := &D.A{} 31 | rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL} 32 | ip := fakePool.Lookup(host) 33 | rr.A = ip 34 | msg := r.Copy() 35 | msg.Answer = []D.RR{rr} 36 | 37 | setMsgTTL(msg, 1) 38 | msg.SetReply(r) 39 | _ = w.WriteMsg(msg) 40 | return 41 | } 42 | } 43 | } 44 | 45 | func withResolver(resolver *Resolver) handler { 46 | return func(w D.ResponseWriter, r *D.Msg) { 47 | msg, err := resolver.Exchange(r) 48 | if err != nil { 49 | q := r.Question[0] 50 | log.Debugln("[DNS Server] Exchange %s failed: %v", q.String(), err) 51 | D.HandleFailed(w, r) 52 | return 53 | } 54 | msg.SetReply(r) 55 | _ = w.WriteMsg(msg) 56 | return 57 | } 58 | } 59 | 60 | func compose(middlewares []middleware, endpoint handler) handler { 61 | length := len(middlewares) 62 | h := endpoint 63 | for i := length - 1; i >= 0; i-- { 64 | middleware := middlewares[i] 65 | h = middleware(h) 66 | } 67 | return h 68 | } 69 | 70 | func newHandler(resolver *Resolver) handler { 71 | middlewares := []middleware{} 72 | 73 | if resolver.IsFakeIP() { 74 | middlewares = append(middlewares, withFakeIP(resolver.pool)) 75 | } 76 | 77 | return compose(middlewares, withResolver(resolver)) 78 | } 79 | -------------------------------------------------------------------------------- /dns/server.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "net" 5 | 6 | D "github.com/miekg/dns" 7 | ) 8 | 9 | var ( 10 | address string 11 | server = &Server{} 12 | 13 | dnsDefaultTTL uint32 = 600 14 | ) 15 | 16 | type Server struct { 17 | *D.Server 18 | handler handler 19 | } 20 | 21 | func (s *Server) ServeDNS(w D.ResponseWriter, r *D.Msg) { 22 | if len(r.Question) == 0 { 23 | D.HandleFailed(w, r) 24 | return 25 | } 26 | 27 | s.handler(w, r) 28 | } 29 | 30 | func (s *Server) setHandler(handler handler) { 31 | s.handler = handler 32 | } 33 | 34 | func ReCreateServer(addr string, resolver *Resolver) error { 35 | if addr == address && resolver != nil { 36 | handler := newHandler(resolver) 37 | server.setHandler(handler) 38 | return nil 39 | } 40 | 41 | if server.Server != nil { 42 | server.Shutdown() 43 | address = "" 44 | } 45 | 46 | _, port, err := net.SplitHostPort(addr) 47 | if port == "0" || port == "" || err != nil { 48 | return nil 49 | } 50 | 51 | udpAddr, err := net.ResolveUDPAddr("udp", addr) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | p, err := net.ListenUDP("udp", udpAddr) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | address = addr 62 | handler := newHandler(resolver) 63 | server = &Server{handler: handler} 64 | server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server} 65 | 66 | go func() { 67 | server.ActivateAndServe() 68 | }() 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /dns/util.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "errors" 7 | "time" 8 | 9 | "github.com/zu1k/clashr/common/cache" 10 | "github.com/zu1k/clashr/log" 11 | yaml "gopkg.in/yaml.v2" 12 | 13 | D "github.com/miekg/dns" 14 | ) 15 | 16 | var ( 17 | // EnhancedModeMapping is a mapping for EnhancedMode enum 18 | EnhancedModeMapping = map[string]EnhancedMode{ 19 | NORMAL.String(): NORMAL, 20 | FAKEIP.String(): FAKEIP, 21 | MAPPING.String(): MAPPING, 22 | } 23 | ) 24 | 25 | const ( 26 | NORMAL EnhancedMode = iota 27 | FAKEIP 28 | MAPPING 29 | ) 30 | 31 | type EnhancedMode int 32 | 33 | // UnmarshalYAML unserialize EnhancedMode with yaml 34 | func (e *EnhancedMode) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 | var tp string 36 | if err := unmarshal(&tp); err != nil { 37 | return err 38 | } 39 | mode, exist := EnhancedModeMapping[tp] 40 | if !exist { 41 | return errors.New("invalid mode") 42 | } 43 | *e = mode 44 | return nil 45 | } 46 | 47 | // MarshalYAML serialize EnhancedMode with yaml 48 | func (e EnhancedMode) MarshalYAML() ([]byte, error) { 49 | return yaml.Marshal(e.String()) 50 | } 51 | 52 | // UnmarshalJSON unserialize EnhancedMode with json 53 | func (e *EnhancedMode) UnmarshalJSON(data []byte) error { 54 | var tp string 55 | json.Unmarshal(data, &tp) 56 | mode, exist := EnhancedModeMapping[tp] 57 | if !exist { 58 | return errors.New("invalid mode") 59 | } 60 | *e = mode 61 | return nil 62 | } 63 | 64 | // MarshalJSON serialize EnhancedMode with json 65 | func (e EnhancedMode) MarshalJSON() ([]byte, error) { 66 | return json.Marshal(e.String()) 67 | } 68 | 69 | func (e EnhancedMode) String() string { 70 | switch e { 71 | case NORMAL: 72 | return "normal" 73 | case FAKEIP: 74 | return "fake-ip" 75 | case MAPPING: 76 | return "redir-host" 77 | default: 78 | return "unknown" 79 | } 80 | } 81 | 82 | func putMsgToCache(c *cache.Cache, key string, msg *D.Msg) { 83 | var ttl time.Duration 84 | if len(msg.Answer) != 0 { 85 | ttl = time.Duration(msg.Answer[0].Header().Ttl) * time.Second 86 | } else if len(msg.Ns) != 0 { 87 | ttl = time.Duration(msg.Ns[0].Header().Ttl) * time.Second 88 | } else if len(msg.Extra) != 0 { 89 | ttl = time.Duration(msg.Extra[0].Header().Ttl) * time.Second 90 | } else { 91 | log.Debugln("[DNS] response msg error: %#v", msg) 92 | return 93 | } 94 | 95 | c.Put(key, msg.Copy(), ttl) 96 | } 97 | 98 | func setMsgTTL(msg *D.Msg, ttl uint32) { 99 | for _, answer := range msg.Answer { 100 | answer.Header().Ttl = ttl 101 | } 102 | 103 | for _, ns := range msg.Ns { 104 | ns.Header().Ttl = ttl 105 | } 106 | 107 | for _, extra := range msg.Extra { 108 | extra.Header().Ttl = ttl 109 | } 110 | } 111 | 112 | func isIPRequest(q D.Question) bool { 113 | if q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA) { 114 | return true 115 | } 116 | return false 117 | } 118 | 119 | func transform(servers []NameServer) []resolver { 120 | ret := []resolver{} 121 | for _, s := range servers { 122 | if s.Net == "https" { 123 | ret = append(ret, &dohClient{url: s.Addr}) 124 | continue 125 | } 126 | 127 | ret = append(ret, &client{ 128 | Client: &D.Client{ 129 | Net: s.Net, 130 | TLSConfig: &tls.Config{ 131 | ClientSessionCache: globalSessionCache, 132 | // alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6 133 | NextProtos: []string{"dns"}, 134 | }, 135 | UDPSize: 4096, 136 | }, 137 | Address: s.Addr, 138 | }) 139 | } 140 | return ret 141 | } 142 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengzhengkevin/clashr/9c821b6c24a2c1102418b198172a29513c52684d/docs/logo.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zu1k/clashr 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/Dreamacro/go-shadowsocks2 v0.1.5-0.20191012162057-46254afc8b68 7 | github.com/dgryski/go-camellia v0.0.0-20140412174459-3be6b3054dd1 // indirect 8 | github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb // indirect 9 | github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 // indirect 10 | github.com/eapache/queue v1.1.0 // indirect 11 | github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect 12 | github.com/go-chi/chi v4.0.2+incompatible 13 | github.com/go-chi/cors v1.0.0 14 | github.com/go-chi/render v1.0.1 15 | github.com/gofrs/uuid v3.2.0+incompatible 16 | github.com/gorilla/websocket v1.4.1 17 | github.com/miekg/dns v1.1.22 18 | github.com/oschwald/geoip2-golang v1.3.0 19 | github.com/oschwald/maxminddb-golang v1.5.0 // indirect 20 | github.com/sirupsen/logrus v1.4.2 21 | github.com/stretchr/testify v1.4.0 22 | github.com/zu1k/gossr v0.0.0-20190917142441-e5497022713d 23 | gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c // indirect 24 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 25 | golang.org/x/net v0.0.0-20191011234655-491137f69257 26 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e 27 | gopkg.in/eapache/channels.v1 v1.1.0 28 | gopkg.in/yaml.v2 v2.2.2 29 | ) 30 | -------------------------------------------------------------------------------- /hub/executor/executor.go: -------------------------------------------------------------------------------- 1 | package executor 2 | 3 | import ( 4 | "github.com/zu1k/clashr/component/auth" 5 | trie "github.com/zu1k/clashr/component/domain-trie" 6 | "github.com/zu1k/clashr/config" 7 | C "github.com/zu1k/clashr/constant" 8 | "github.com/zu1k/clashr/dns" 9 | "github.com/zu1k/clashr/log" 10 | P "github.com/zu1k/clashr/proxy" 11 | authStore "github.com/zu1k/clashr/proxy/auth" 12 | T "github.com/zu1k/clashr/tunnel" 13 | ) 14 | 15 | // Parse config with default config path 16 | func Parse() (*config.Config, error) { 17 | return ParseWithPath(C.Path.Config()) 18 | } 19 | 20 | // ParseWithPath parse config with custom config path 21 | func ParseWithPath(path string) (*config.Config, error) { 22 | return config.Parse(path) 23 | } 24 | 25 | // ApplyConfig dispatch configure to all parts 26 | func ApplyConfig(cfg *config.Config, force bool) { 27 | updateUsers(cfg.Users) 28 | if force { 29 | updateGeneral(cfg.General) 30 | } 31 | updateProxies(cfg.Proxies) 32 | updateRules(cfg.Rules) 33 | updateDNS(cfg.DNS) 34 | updateHosts(cfg.Hosts) 35 | updateExperimental(cfg.Experimental) 36 | } 37 | 38 | func GetGeneral() *config.General { 39 | ports := P.GetPorts() 40 | authenticator := []string{} 41 | if auth := authStore.Authenticator(); auth != nil { 42 | authenticator = auth.Users() 43 | } 44 | 45 | general := &config.General{ 46 | Port: ports.Port, 47 | SocksPort: ports.SocksPort, 48 | RedirPort: ports.RedirPort, 49 | Authentication: authenticator, 50 | AllowLan: P.AllowLan(), 51 | BindAddress: P.BindAddress(), 52 | Mode: T.Instance().Mode(), 53 | LogLevel: log.Level(), 54 | } 55 | 56 | return general 57 | } 58 | 59 | func updateExperimental(c *config.Experimental) { 60 | T.Instance().UpdateExperimental(c.IgnoreResolveFail) 61 | } 62 | 63 | func updateDNS(c *config.DNS) { 64 | if c.Enable == false { 65 | dns.DefaultResolver = nil 66 | _ = dns.ReCreateServer("", nil) 67 | return 68 | } 69 | r := dns.New(dns.Config{ 70 | Main: c.NameServer, 71 | Fallback: c.Fallback, 72 | IPv6: c.IPv6, 73 | EnhancedMode: c.EnhancedMode, 74 | Pool: c.FakeIPRange, 75 | FallbackFilter: dns.FallbackFilter{ 76 | GeoIP: c.FallbackFilter.GeoIP, 77 | IPCIDR: c.FallbackFilter.IPCIDR, 78 | }, 79 | }) 80 | dns.DefaultResolver = r 81 | if err := dns.ReCreateServer(c.Listen, r); err != nil { 82 | log.Errorln("Start DNS server error: %s", err.Error()) 83 | return 84 | } 85 | 86 | if c.Listen != "" { 87 | log.Infoln("DNS server listening at: %s", c.Listen) 88 | } 89 | } 90 | 91 | func updateHosts(tree *trie.Trie) { 92 | dns.DefaultHosts = tree 93 | } 94 | 95 | func updateProxies(proxies map[string]C.Proxy) { 96 | tunnel := T.Instance() 97 | oldProxies := tunnel.Proxies() 98 | 99 | // close proxy group goroutine 100 | for _, proxy := range oldProxies { 101 | proxy.Destroy() 102 | } 103 | 104 | tunnel.UpdateProxies(proxies) 105 | } 106 | 107 | func updateRules(rules []C.Rule) { 108 | T.Instance().UpdateRules(rules) 109 | } 110 | 111 | func updateGeneral(general *config.General) { 112 | log.SetLevel(general.LogLevel) 113 | T.Instance().SetMode(general.Mode) 114 | 115 | allowLan := general.AllowLan 116 | P.SetAllowLan(allowLan) 117 | 118 | bindAddress := general.BindAddress 119 | P.SetBindAddress(bindAddress) 120 | 121 | if err := P.ReCreateHTTP(general.Port); err != nil { 122 | log.Errorln("Start HTTP server error: %s", err.Error()) 123 | } 124 | 125 | if err := P.ReCreateSocks(general.SocksPort); err != nil { 126 | log.Errorln("Start SOCKS5 server error: %s", err.Error()) 127 | } 128 | 129 | if err := P.ReCreateRedir(general.RedirPort); err != nil { 130 | log.Errorln("Start Redir server error: %s", err.Error()) 131 | } 132 | } 133 | 134 | func updateUsers(users []auth.AuthUser) { 135 | authenticator := auth.NewAuthenticator(users) 136 | authStore.SetAuthenticator(authenticator) 137 | if authenticator != nil { 138 | log.Infoln("Authentication of local server updated") 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /hub/hub.go: -------------------------------------------------------------------------------- 1 | package hub 2 | 3 | import ( 4 | "github.com/zu1k/clashr/hub/executor" 5 | "github.com/zu1k/clashr/hub/route" 6 | ) 7 | 8 | // Parse call at the beginning of clash 9 | func Parse() error { 10 | cfg, err := executor.Parse() 11 | if err != nil { 12 | return err 13 | } 14 | 15 | if cfg.General.ExternalUI != "" { 16 | route.SetUIPath(cfg.General.ExternalUI) 17 | } 18 | 19 | if cfg.General.ExternalController != "" { 20 | go route.Start(cfg.General.ExternalController, cfg.General.Secret) 21 | } 22 | 23 | executor.ApplyConfig(cfg, true) 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /hub/route/configs.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "net/http" 5 | "path/filepath" 6 | 7 | "github.com/zu1k/clashr/hub/executor" 8 | "github.com/zu1k/clashr/log" 9 | P "github.com/zu1k/clashr/proxy" 10 | T "github.com/zu1k/clashr/tunnel" 11 | 12 | "github.com/go-chi/chi" 13 | "github.com/go-chi/render" 14 | ) 15 | 16 | func configRouter() http.Handler { 17 | r := chi.NewRouter() 18 | r.Get("/", getConfigs) 19 | r.Put("/", updateConfigs) 20 | r.Patch("/", patchConfigs) 21 | return r 22 | } 23 | 24 | type configSchema struct { 25 | Port *int `json:"port"` 26 | SocksPort *int `json:"socks-port"` 27 | RedirPort *int `json:"redir-port"` 28 | AllowLan *bool `json:"allow-lan"` 29 | BindAddress *string `json:"bind-address"` 30 | Mode *T.Mode `json:"mode"` 31 | LogLevel *log.LogLevel `json:"log-level"` 32 | } 33 | 34 | func getConfigs(w http.ResponseWriter, r *http.Request) { 35 | general := executor.GetGeneral() 36 | render.JSON(w, r, general) 37 | } 38 | 39 | func pointerOrDefault(p *int, def int) int { 40 | if p != nil { 41 | return *p 42 | } 43 | 44 | return def 45 | } 46 | 47 | func patchConfigs(w http.ResponseWriter, r *http.Request) { 48 | general := &configSchema{} 49 | if err := render.DecodeJSON(r.Body, general); err != nil { 50 | render.Status(r, http.StatusBadRequest) 51 | render.JSON(w, r, ErrBadRequest) 52 | return 53 | } 54 | 55 | if general.AllowLan != nil { 56 | P.SetAllowLan(*general.AllowLan) 57 | } 58 | 59 | if general.BindAddress != nil { 60 | P.SetBindAddress(*general.BindAddress) 61 | } 62 | 63 | ports := P.GetPorts() 64 | _ = P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port)) 65 | _ = P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort)) 66 | _ = P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort)) 67 | 68 | if general.Mode != nil { 69 | T.Instance().SetMode(*general.Mode) 70 | } 71 | 72 | if general.LogLevel != nil { 73 | log.SetLevel(*general.LogLevel) 74 | } 75 | 76 | render.NoContent(w, r) 77 | } 78 | 79 | type updateConfigRequest struct { 80 | Path string `json:"path"` 81 | } 82 | 83 | func updateConfigs(w http.ResponseWriter, r *http.Request) { 84 | req := updateConfigRequest{} 85 | if err := render.DecodeJSON(r.Body, &req); err != nil { 86 | render.Status(r, http.StatusBadRequest) 87 | render.JSON(w, r, ErrBadRequest) 88 | return 89 | } 90 | 91 | if !filepath.IsAbs(req.Path) { 92 | render.Status(r, http.StatusBadRequest) 93 | render.JSON(w, r, newError("path is not a absoluted path")) 94 | return 95 | } 96 | 97 | force := r.URL.Query().Get("force") == "true" 98 | cfg, err := executor.ParseWithPath(req.Path) 99 | if err != nil { 100 | render.Status(r, http.StatusBadRequest) 101 | render.JSON(w, r, newError(err.Error())) 102 | return 103 | } 104 | 105 | executor.ApplyConfig(cfg, force) 106 | render.NoContent(w, r) 107 | } 108 | -------------------------------------------------------------------------------- /hub/route/ctxkeys.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | var ( 4 | CtxKeyProxyName = contextKey("proxy name") 5 | CtxKeyProxy = contextKey("proxy") 6 | ) 7 | 8 | type contextKey string 9 | 10 | func (c contextKey) String() string { 11 | return "clash context key " + string(c) 12 | } 13 | -------------------------------------------------------------------------------- /hub/route/errors.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | var ( 4 | ErrUnauthorized = newError("Unauthorized") 5 | ErrBadRequest = newError("Body invalid") 6 | ErrForbidden = newError("Forbidden") 7 | ErrNotFound = newError("Resource not found") 8 | ErrRequestTimeout = newError("Timeout") 9 | ) 10 | 11 | // HTTPError is custom HTTP error for API 12 | type HTTPError struct { 13 | Message string `json:"message"` 14 | } 15 | 16 | func (e *HTTPError) Error() string { 17 | return e.Message 18 | } 19 | 20 | func newError(msg string) *HTTPError { 21 | return &HTTPError{Message: msg} 22 | } 23 | -------------------------------------------------------------------------------- /hub/route/proxies.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | "time" 10 | 11 | A "github.com/zu1k/clashr/adapters/outbound" 12 | C "github.com/zu1k/clashr/constant" 13 | T "github.com/zu1k/clashr/tunnel" 14 | 15 | "github.com/go-chi/chi" 16 | "github.com/go-chi/render" 17 | ) 18 | 19 | func proxyRouter() http.Handler { 20 | r := chi.NewRouter() 21 | r.Get("/", getProxies) 22 | 23 | r.Route("/{name}", func(r chi.Router) { 24 | r.Use(parseProxyName, findProxyByName) 25 | r.Get("/", getProxy) 26 | r.Get("/delay", getProxyDelay) 27 | r.Put("/", updateProxy) 28 | }) 29 | return r 30 | } 31 | 32 | // When name is composed of a partial escape string, Golang does not unescape it 33 | func parseProxyName(next http.Handler) http.Handler { 34 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 35 | name := chi.URLParam(r, "name") 36 | if newName, err := url.PathUnescape(name); err == nil { 37 | name = newName 38 | } 39 | ctx := context.WithValue(r.Context(), CtxKeyProxyName, name) 40 | next.ServeHTTP(w, r.WithContext(ctx)) 41 | }) 42 | } 43 | 44 | func findProxyByName(next http.Handler) http.Handler { 45 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 46 | name := r.Context().Value(CtxKeyProxyName).(string) 47 | proxies := T.Instance().Proxies() 48 | proxy, exist := proxies[name] 49 | if !exist { 50 | render.Status(r, http.StatusNotFound) 51 | render.JSON(w, r, ErrNotFound) 52 | return 53 | } 54 | 55 | ctx := context.WithValue(r.Context(), CtxKeyProxy, proxy) 56 | next.ServeHTTP(w, r.WithContext(ctx)) 57 | }) 58 | } 59 | 60 | func getProxies(w http.ResponseWriter, r *http.Request) { 61 | proxies := T.Instance().Proxies() 62 | render.JSON(w, r, render.M{ 63 | "proxies": proxies, 64 | }) 65 | } 66 | 67 | func getProxy(w http.ResponseWriter, r *http.Request) { 68 | proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) 69 | render.JSON(w, r, proxy) 70 | } 71 | 72 | type UpdateProxyRequest struct { 73 | Name string `json:"name"` 74 | } 75 | 76 | func updateProxy(w http.ResponseWriter, r *http.Request) { 77 | req := UpdateProxyRequest{} 78 | if err := render.DecodeJSON(r.Body, &req); err != nil { 79 | render.Status(r, http.StatusBadRequest) 80 | render.JSON(w, r, ErrBadRequest) 81 | return 82 | } 83 | 84 | proxy := r.Context().Value(CtxKeyProxy).(*A.Proxy) 85 | selector, ok := proxy.ProxyAdapter.(*A.Selector) 86 | if !ok { 87 | render.Status(r, http.StatusBadRequest) 88 | render.JSON(w, r, newError("Must be a Selector")) 89 | return 90 | } 91 | 92 | if err := selector.Set(req.Name); err != nil { 93 | render.Status(r, http.StatusBadRequest) 94 | render.JSON(w, r, newError(fmt.Sprintf("Selector update error: %s", err.Error()))) 95 | return 96 | } 97 | 98 | render.NoContent(w, r) 99 | } 100 | 101 | func getProxyDelay(w http.ResponseWriter, r *http.Request) { 102 | query := r.URL.Query() 103 | url := query.Get("url") 104 | timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 16) 105 | if err != nil { 106 | render.Status(r, http.StatusBadRequest) 107 | render.JSON(w, r, ErrBadRequest) 108 | return 109 | } 110 | 111 | proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) 112 | 113 | ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout)) 114 | defer cancel() 115 | 116 | delay, err := proxy.URLTest(ctx, url) 117 | if ctx.Err() != nil { 118 | render.Status(r, http.StatusGatewayTimeout) 119 | render.JSON(w, r, ErrRequestTimeout) 120 | return 121 | } 122 | 123 | if err != nil || delay == 0 { 124 | render.Status(r, http.StatusServiceUnavailable) 125 | render.JSON(w, r, newError("An error occurred in the delay test")) 126 | return 127 | } 128 | 129 | render.JSON(w, r, render.M{ 130 | "delay": delay, 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /hub/route/rules.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "net/http" 5 | 6 | T "github.com/zu1k/clashr/tunnel" 7 | 8 | "github.com/go-chi/chi" 9 | "github.com/go-chi/render" 10 | ) 11 | 12 | func ruleRouter() http.Handler { 13 | r := chi.NewRouter() 14 | r.Get("/", getRules) 15 | return r 16 | } 17 | 18 | type Rule struct { 19 | Type string `json:"type"` 20 | Payload string `json:"payload"` 21 | Proxy string `json:"proxy"` 22 | } 23 | 24 | func getRules(w http.ResponseWriter, r *http.Request) { 25 | rawRules := T.Instance().Rules() 26 | 27 | rules := []Rule{} 28 | for _, rule := range rawRules { 29 | rules = append(rules, Rule{ 30 | Type: rule.RuleType().String(), 31 | Payload: rule.Payload(), 32 | Proxy: rule.Adapter(), 33 | }) 34 | } 35 | 36 | render.JSON(w, r, render.M{ 37 | "rules": rules, 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /hub/route/sysproxy.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/go-chi/chi" 5 | "github.com/go-chi/render" 6 | "github.com/zu1k/clashr/hub/executor" 7 | "net/http" 8 | "os/exec" 9 | "strconv" 10 | ) 11 | 12 | const ( 13 | Gsettings = "gsettings" 14 | Set = "set" 15 | 16 | SystemProxy = "org.gnome.system.proxy" 17 | SystemProxyHttp = "org.gnome.system.proxy.http" 18 | SystemProxyHttps = "org.gnome.system.proxy.https" 19 | SystemProxySocks = "org.gnome.system.proxy.socks" 20 | 21 | ProxyHost = "host" 22 | ProxyPort = "port" 23 | 24 | ProxyMode = "mode" 25 | None = "none" 26 | Manual = "manual" 27 | ) 28 | 29 | func systemProxySettingRouter() http.Handler { 30 | r := chi.NewRouter() 31 | r.Get("/http", setSystemProxyHttp) 32 | r.Get("/socks", setSystemProxySocks) 33 | r.Get("/none", closeSystemProxy) 34 | return r 35 | } 36 | 37 | func setSystemProxyHttp(w http.ResponseWriter, r *http.Request) { 38 | config := executor.GetGeneral() 39 | err := exec.Command(Gsettings, Set, SystemProxyHttp, ProxyHost, config.BindAddress).Run() 40 | err = exec.Command(Gsettings, Set, SystemProxyHttp, ProxyPort, strconv.Itoa(config.Port)).Run() 41 | err = exec.Command(Gsettings, Set, SystemProxy, ProxyMode, Manual).Run() 42 | if err != nil { 43 | render.JSON(w, r, render.M{ 44 | "success": false, 45 | "err": err.Error(), 46 | }) 47 | } else { 48 | render.JSON(w, r, render.M{ 49 | "success": true, 50 | }) 51 | } 52 | } 53 | 54 | func setSystemProxySocks(w http.ResponseWriter, r *http.Request) { 55 | config := executor.GetGeneral() 56 | err := exec.Command(Gsettings, Set, SystemProxySocks, ProxyHost, config.BindAddress).Run() 57 | err = exec.Command(Gsettings, Set, SystemProxySocks, ProxyPort, strconv.Itoa(config.SocksPort)).Run() 58 | err = exec.Command(Gsettings, Set, SystemProxy, ProxyMode, Manual).Run() 59 | if err != nil { 60 | render.JSON(w, r, render.M{ 61 | "success": false, 62 | "err": err.Error(), 63 | }) 64 | } else { 65 | render.JSON(w, r, render.M{ 66 | "success": true, 67 | }) 68 | } 69 | } 70 | 71 | func closeSystemProxy(w http.ResponseWriter, r *http.Request) { 72 | err := exec.Command(Gsettings, Set, SystemProxy, ProxyMode, None).Run() 73 | if err != nil { 74 | render.JSON(w, r, render.M{ 75 | "success": false, 76 | "err": err.Error(), 77 | }) 78 | } else { 79 | render.JSON(w, r, render.M{ 80 | "success": true, 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /log/level.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | var ( 9 | // LogLevelMapping is a mapping for LogLevel enum 10 | LogLevelMapping = map[string]LogLevel{ 11 | ERROR.String(): ERROR, 12 | WARNING.String(): WARNING, 13 | INFO.String(): INFO, 14 | DEBUG.String(): DEBUG, 15 | SILENT.String(): SILENT, 16 | } 17 | ) 18 | 19 | const ( 20 | DEBUG LogLevel = iota 21 | INFO 22 | WARNING 23 | ERROR 24 | SILENT 25 | ) 26 | 27 | type LogLevel int 28 | 29 | // UnmarshalYAML unserialize LogLevel with yaml 30 | func (l *LogLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { 31 | var tp string 32 | unmarshal(&tp) 33 | level, exist := LogLevelMapping[tp] 34 | if !exist { 35 | return errors.New("invalid mode") 36 | } 37 | *l = level 38 | return nil 39 | } 40 | 41 | // UnmarshalJSON unserialize LogLevel with json 42 | func (l *LogLevel) UnmarshalJSON(data []byte) error { 43 | var tp string 44 | json.Unmarshal(data, &tp) 45 | level, exist := LogLevelMapping[tp] 46 | if !exist { 47 | return errors.New("invalid mode") 48 | } 49 | *l = level 50 | return nil 51 | } 52 | 53 | // MarshalJSON serialize LogLevel with json 54 | func (l LogLevel) MarshalJSON() ([]byte, error) { 55 | return json.Marshal(l.String()) 56 | } 57 | 58 | func (l LogLevel) String() string { 59 | switch l { 60 | case INFO: 61 | return "info" 62 | case WARNING: 63 | return "warning" 64 | case ERROR: 65 | return "error" 66 | case DEBUG: 67 | return "debug" 68 | case SILENT: 69 | return "silent" 70 | default: 71 | return "unknown" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/zu1k/clashr/common/observable" 8 | 9 | log "github.com/sirupsen/logrus" 10 | ) 11 | 12 | var ( 13 | logCh = make(chan interface{}) 14 | source = observable.NewObservable(logCh) 15 | level = INFO 16 | ) 17 | 18 | func init() { 19 | log.SetOutput(os.Stdout) 20 | log.SetLevel(log.DebugLevel) 21 | } 22 | 23 | type Event struct { 24 | LogLevel LogLevel 25 | Payload string 26 | } 27 | 28 | func (e *Event) Type() string { 29 | return e.LogLevel.String() 30 | } 31 | 32 | func Infoln(format string, v ...interface{}) { 33 | event := newLog(INFO, format, v...) 34 | logCh <- event 35 | print(event) 36 | } 37 | 38 | func Warnln(format string, v ...interface{}) { 39 | event := newLog(WARNING, format, v...) 40 | logCh <- event 41 | print(event) 42 | } 43 | 44 | func Errorln(format string, v ...interface{}) { 45 | event := newLog(ERROR, format, v...) 46 | logCh <- event 47 | print(event) 48 | } 49 | 50 | func Debugln(format string, v ...interface{}) { 51 | event := newLog(DEBUG, format, v...) 52 | logCh <- event 53 | print(event) 54 | } 55 | 56 | func Fatalln(format string, v ...interface{}) { 57 | log.Fatalf(format, v...) 58 | } 59 | 60 | func Subscribe() observable.Subscription { 61 | sub, _ := source.Subscribe() 62 | return sub 63 | } 64 | 65 | func UnSubscribe(sub observable.Subscription) { 66 | source.UnSubscribe(sub) 67 | return 68 | } 69 | 70 | func Level() LogLevel { 71 | return level 72 | } 73 | 74 | func SetLevel(newLevel LogLevel) { 75 | level = newLevel 76 | } 77 | 78 | func print(data *Event) { 79 | if data.LogLevel < level { 80 | return 81 | } 82 | 83 | switch data.LogLevel { 84 | case INFO: 85 | log.Infoln(data.Payload) 86 | case WARNING: 87 | log.Warnln(data.Payload) 88 | case ERROR: 89 | log.Errorln(data.Payload) 90 | case DEBUG: 91 | log.Debugln(data.Payload) 92 | } 93 | } 94 | 95 | func newLog(logLevel LogLevel, format string, v ...interface{}) *Event { 96 | return &Event{ 97 | LogLevel: logLevel, 98 | Payload: fmt.Sprintf(format, v...), 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "path/filepath" 9 | "runtime" 10 | "syscall" 11 | 12 | "github.com/zu1k/clashr/config" 13 | C "github.com/zu1k/clashr/constant" 14 | "github.com/zu1k/clashr/hub" 15 | 16 | log "github.com/sirupsen/logrus" 17 | ) 18 | 19 | var ( 20 | version bool 21 | homedir string 22 | ) 23 | 24 | func init() { 25 | flag.StringVar(&homedir, "d", "", "set configuration directory") 26 | flag.BoolVar(&version, "v", false, "show current version of clash") 27 | flag.Parse() 28 | } 29 | 30 | func main() { 31 | defer func() { 32 | if err := recover(); err != nil { 33 | fmt.Println("panic recover") 34 | go main() 35 | } 36 | }() 37 | 38 | if version { 39 | fmt.Printf("Clash %s %s %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, C.BuildTime) 40 | return 41 | } 42 | 43 | if homedir != "" { 44 | if !filepath.IsAbs(homedir) { 45 | currentDir, _ := os.Getwd() 46 | homedir = filepath.Join(currentDir, homedir) 47 | } 48 | C.SetHomeDir(homedir) 49 | } 50 | 51 | if err := config.Init(C.Path.HomeDir()); err != nil { 52 | log.Fatalf("Initial configuration directory error: %s", err.Error()) 53 | } 54 | 55 | if err := hub.Parse(); err != nil { 56 | log.Fatalf("Parse config error: %s", err.Error()) 57 | } 58 | 59 | sigCh := make(chan os.Signal, 1) 60 | signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) 61 | <-sigCh 62 | } 63 | -------------------------------------------------------------------------------- /proxy/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/zu1k/clashr/component/auth" 5 | ) 6 | 7 | var ( 8 | authenticator auth.Authenticator 9 | ) 10 | 11 | func Authenticator() auth.Authenticator { 12 | return authenticator 13 | } 14 | 15 | func SetAuthenticator(au auth.Authenticator) { 16 | authenticator = au 17 | } 18 | -------------------------------------------------------------------------------- /proxy/http/server.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "net" 7 | "net/http" 8 | "strings" 9 | "time" 10 | 11 | adapters "github.com/zu1k/clashr/adapters/inbound" 12 | "github.com/zu1k/clashr/common/cache" 13 | "github.com/zu1k/clashr/component/auth" 14 | "github.com/zu1k/clashr/log" 15 | authStore "github.com/zu1k/clashr/proxy/auth" 16 | "github.com/zu1k/clashr/tunnel" 17 | ) 18 | 19 | var ( 20 | tun = tunnel.Instance() 21 | ) 22 | 23 | type HttpListener struct { 24 | net.Listener 25 | address string 26 | closed bool 27 | cache *cache.Cache 28 | } 29 | 30 | func NewHttpProxy(addr string) (*HttpListener, error) { 31 | l, err := net.Listen("tcp", addr) 32 | if err != nil { 33 | return nil, err 34 | } 35 | hl := &HttpListener{l, addr, false, cache.New(30 * time.Second)} 36 | 37 | go func() { 38 | log.Infoln("HTTP proxy listening at: %s", addr) 39 | 40 | for { 41 | c, err := hl.Accept() 42 | if err != nil { 43 | if hl.closed { 44 | break 45 | } 46 | continue 47 | } 48 | go handleConn(c, hl.cache) 49 | } 50 | }() 51 | 52 | return hl, nil 53 | } 54 | 55 | func (l *HttpListener) Close() { 56 | l.closed = true 57 | _ = l.Listener.Close() 58 | } 59 | 60 | func (l *HttpListener) Address() string { 61 | return l.address 62 | } 63 | 64 | func canActivate(loginStr string, authenticator auth.Authenticator, cache *cache.Cache) (ret bool) { 65 | if result := cache.Get(loginStr); result != nil { 66 | ret = result.(bool) 67 | } 68 | loginData, err := base64.StdEncoding.DecodeString(loginStr) 69 | login := strings.Split(string(loginData), ":") 70 | ret = err == nil && len(login) == 2 && authenticator.Verify(login[0], login[1]) 71 | 72 | cache.Put(loginStr, ret, time.Minute) 73 | return 74 | } 75 | 76 | func handleConn(conn net.Conn, cache *cache.Cache) { 77 | br := bufio.NewReader(conn) 78 | request, err := http.ReadRequest(br) 79 | if err != nil || request.URL.Host == "" { 80 | _ = conn.Close() 81 | return 82 | } 83 | 84 | authenticator := authStore.Authenticator() 85 | if authenticator != nil { 86 | if authStrings := strings.Split(request.Header.Get("Proxy-Authorization"), " "); len(authStrings) != 2 { 87 | _, err = conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic\r\n\r\n")) 88 | _ = conn.Close() 89 | return 90 | } else if !canActivate(authStrings[1], authenticator, cache) { 91 | _, _ = conn.Write([]byte("HTTP/1.1 403 Forbidden\r\n\r\n")) 92 | log.Infoln("Auth failed from %s", conn.RemoteAddr().String()) 93 | _ = conn.Close() 94 | return 95 | } 96 | } 97 | 98 | if request.Method == http.MethodConnect { 99 | _, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) 100 | if err != nil { 101 | return 102 | } 103 | tun.Add(adapters.NewHTTPS(request, conn)) 104 | return 105 | } 106 | 107 | tun.Add(adapters.NewHTTP(request, conn)) 108 | } 109 | -------------------------------------------------------------------------------- /proxy/listener.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/zu1k/clashr/proxy/http" 9 | "github.com/zu1k/clashr/proxy/redir" 10 | "github.com/zu1k/clashr/proxy/socks" 11 | ) 12 | 13 | var ( 14 | allowLan = false 15 | bindAddress = "*" 16 | 17 | socksListener *socks.SockListener 18 | socksUDPListener *socks.SockUDPListener 19 | httpListener *http.HttpListener 20 | redirListener *redir.RedirListener 21 | ) 22 | 23 | type listener interface { 24 | Close() 25 | Address() string 26 | } 27 | 28 | type Ports struct { 29 | Port int `json:"port"` 30 | SocksPort int `json:"socks-port"` 31 | RedirPort int `json:"redir-port"` 32 | } 33 | 34 | func AllowLan() bool { 35 | return allowLan 36 | } 37 | 38 | func BindAddress() string { 39 | return bindAddress 40 | } 41 | 42 | func SetAllowLan(al bool) { 43 | allowLan = al 44 | } 45 | 46 | func SetBindAddress(host string) { 47 | bindAddress = host 48 | } 49 | 50 | func ReCreateHTTP(port int) error { 51 | addr := genAddr(bindAddress, port, allowLan) 52 | 53 | if httpListener != nil { 54 | if httpListener.Address() == addr { 55 | return nil 56 | } 57 | httpListener.Close() 58 | httpListener = nil 59 | } 60 | 61 | if portIsZero(addr) { 62 | return nil 63 | } 64 | 65 | var err error 66 | httpListener, err = http.NewHttpProxy(addr) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | return nil 72 | } 73 | 74 | func ReCreateSocks(port int) error { 75 | addr := genAddr(bindAddress, port, allowLan) 76 | 77 | if socksListener != nil { 78 | if socksListener.Address() != addr { 79 | socksListener.Close() 80 | socksListener = nil 81 | } 82 | } 83 | 84 | if socksUDPListener != nil { 85 | if socksUDPListener.Address() != addr { 86 | socksUDPListener.Close() 87 | socksUDPListener = nil 88 | } 89 | } 90 | 91 | if portIsZero(addr) { 92 | return nil 93 | } 94 | 95 | tcpListener, err := socks.NewSocksProxy(addr) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | udpListener, err := socks.NewSocksUDPProxy(addr) 101 | if err != nil { 102 | tcpListener.Close() 103 | return err 104 | } 105 | 106 | socksListener = tcpListener 107 | socksUDPListener = udpListener 108 | 109 | return nil 110 | } 111 | 112 | func ReCreateRedir(port int) error { 113 | addr := genAddr(bindAddress, port, allowLan) 114 | 115 | if redirListener != nil { 116 | if redirListener.Address() == addr { 117 | return nil 118 | } 119 | redirListener.Close() 120 | redirListener = nil 121 | } 122 | 123 | if portIsZero(addr) { 124 | return nil 125 | } 126 | 127 | var err error 128 | redirListener, err = redir.NewRedirProxy(addr) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | return nil 134 | } 135 | 136 | // GetPorts return the ports of proxy servers 137 | func GetPorts() *Ports { 138 | ports := &Ports{} 139 | 140 | if httpListener != nil { 141 | _, portStr, _ := net.SplitHostPort(httpListener.Address()) 142 | port, _ := strconv.Atoi(portStr) 143 | ports.Port = port 144 | } 145 | 146 | if socksListener != nil { 147 | _, portStr, _ := net.SplitHostPort(socksListener.Address()) 148 | port, _ := strconv.Atoi(portStr) 149 | ports.SocksPort = port 150 | } 151 | 152 | if redirListener != nil { 153 | _, portStr, _ := net.SplitHostPort(redirListener.Address()) 154 | port, _ := strconv.Atoi(portStr) 155 | ports.RedirPort = port 156 | } 157 | 158 | return ports 159 | } 160 | 161 | func portIsZero(addr string) bool { 162 | _, port, err := net.SplitHostPort(addr) 163 | if port == "0" || port == "" || err != nil { 164 | return true 165 | } 166 | return false 167 | } 168 | 169 | func genAddr(host string, port int, allowLan bool) string { 170 | if allowLan { 171 | if host == "*" { 172 | return fmt.Sprintf(":%d", port) 173 | } else { 174 | return fmt.Sprintf("%s:%d", host, port) 175 | } 176 | } 177 | 178 | return fmt.Sprintf("127.0.0.1:%d", port) 179 | } 180 | -------------------------------------------------------------------------------- /proxy/redir/tcp.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/zu1k/clashr/adapters/inbound" 7 | C "github.com/zu1k/clashr/constant" 8 | "github.com/zu1k/clashr/log" 9 | "github.com/zu1k/clashr/tunnel" 10 | ) 11 | 12 | var ( 13 | tun = tunnel.Instance() 14 | ) 15 | 16 | type RedirListener struct { 17 | net.Listener 18 | address string 19 | closed bool 20 | } 21 | 22 | func NewRedirProxy(addr string) (*RedirListener, error) { 23 | l, err := net.Listen("tcp", addr) 24 | if err != nil { 25 | return nil, err 26 | } 27 | rl := &RedirListener{l, addr, false} 28 | 29 | go func() { 30 | log.Infoln("Redir proxy listening at: %s", addr) 31 | for { 32 | c, err := l.Accept() 33 | if err != nil { 34 | if rl.closed { 35 | break 36 | } 37 | continue 38 | } 39 | go handleRedir(c) 40 | } 41 | }() 42 | 43 | return rl, nil 44 | } 45 | 46 | func (l *RedirListener) Close() { 47 | l.closed = true 48 | _ = l.Listener.Close() 49 | } 50 | 51 | func (l *RedirListener) Address() string { 52 | return l.address 53 | } 54 | 55 | func handleRedir(conn net.Conn) { 56 | target, err := parserPacket(conn) 57 | if err != nil { 58 | _ = conn.Close() 59 | return 60 | } 61 | _ = conn.(*net.TCPConn).SetKeepAlive(true) 62 | tun.Add(adapters.NewSocket(target, conn, C.REDIR, C.TCP)) 63 | } 64 | -------------------------------------------------------------------------------- /proxy/redir/tcp_darwin.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "net" 5 | "syscall" 6 | "unsafe" 7 | 8 | "github.com/zu1k/clashr/component/socks5" 9 | ) 10 | 11 | func parserPacket(c net.Conn) (socks5.Addr, error) { 12 | const ( 13 | PfInout = 0 14 | PfIn = 1 15 | PfOut = 2 16 | IOCOut = 0x40000000 17 | IOCIn = 0x80000000 18 | IOCInOut = IOCIn | IOCOut 19 | IOCPARMMask = 0x1FFF 20 | LEN = 4*16 + 4*4 + 4*1 21 | // #define _IOC(inout,group,num,len) (inout | ((len & IOCPARMMask) << 16) | ((group) << 8) | (num)) 22 | // #define _IOWR(g,n,t) _IOC(IOCInOut, (g), (n), sizeof(t)) 23 | // #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) 24 | DIOCNATLOOK = IOCInOut | ((LEN & IOCPARMMask) << 16) | ('D' << 8) | 23 25 | ) 26 | 27 | fd, err := syscall.Open("/dev/pf", 0, syscall.O_RDONLY) 28 | if err != nil { 29 | return nil, err 30 | } 31 | defer syscall.Close(fd) 32 | 33 | nl := struct { // struct pfioc_natlook 34 | saddr, daddr, rsaddr, rdaddr [16]byte 35 | sxport, dxport, rsxport, rdxport [4]byte 36 | af, proto, protoVariant, direction uint8 37 | }{ 38 | af: syscall.AF_INET, 39 | proto: syscall.IPPROTO_TCP, 40 | direction: PfOut, 41 | } 42 | saddr := c.RemoteAddr().(*net.TCPAddr) 43 | daddr := c.LocalAddr().(*net.TCPAddr) 44 | copy(nl.saddr[:], saddr.IP) 45 | copy(nl.daddr[:], daddr.IP) 46 | nl.sxport[0], nl.sxport[1] = byte(saddr.Port>>8), byte(saddr.Port) 47 | nl.dxport[0], nl.dxport[1] = byte(daddr.Port>>8), byte(daddr.Port) 48 | 49 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 { 50 | return nil, errno 51 | } 52 | 53 | addr := make([]byte, 1+net.IPv4len+2) 54 | addr[0] = socks5.AtypIPv4 55 | copy(addr[1:1+net.IPv4len], nl.rdaddr[:4]) 56 | copy(addr[1+net.IPv4len:], nl.rdxport[:2]) 57 | return addr, nil 58 | } 59 | -------------------------------------------------------------------------------- /proxy/redir/tcp_freebsd.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/zu1k/clashr/component/socks5" 10 | ) 11 | 12 | const ( 13 | SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h 14 | IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h 15 | ) 16 | 17 | func parserPacket(conn net.Conn) (socks5.Addr, error) { 18 | c, ok := conn.(*net.TCPConn) 19 | if !ok { 20 | return nil, errors.New("only work with TCP connection") 21 | } 22 | 23 | rc, err := c.SyscallConn() 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var addr socks5.Addr 29 | 30 | rc.Control(func(fd uintptr) { 31 | addr, err = getorigdst(fd) 32 | }) 33 | 34 | return addr, err 35 | } 36 | 37 | // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 38 | func getorigdst(fd uintptr) (socks5.Addr, error) { 39 | raw := syscall.RawSockaddrInet4{} 40 | siz := unsafe.Sizeof(raw) 41 | _, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0) 42 | if err != 0 { 43 | return nil, err 44 | } 45 | 46 | addr := make([]byte, 1+net.IPv4len+2) 47 | addr[0] = socks5.AtypIPv4 48 | copy(addr[1:1+net.IPv4len], raw.Addr[:]) 49 | port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian 50 | addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] 51 | return addr, nil 52 | } 53 | -------------------------------------------------------------------------------- /proxy/redir/tcp_linux.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/zu1k/clashr/component/socks5" 10 | ) 11 | 12 | const ( 13 | SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h 14 | IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h 15 | ) 16 | 17 | func parserPacket(conn net.Conn) (socks5.Addr, error) { 18 | c, ok := conn.(*net.TCPConn) 19 | if !ok { 20 | return nil, errors.New("only work with TCP connection") 21 | } 22 | 23 | rc, err := c.SyscallConn() 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var addr socks5.Addr 29 | 30 | rc.Control(func(fd uintptr) { 31 | addr, err = getorigdst(fd) 32 | }) 33 | 34 | return addr, err 35 | } 36 | 37 | // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 38 | func getorigdst(fd uintptr) (socks5.Addr, error) { 39 | raw := syscall.RawSockaddrInet4{} 40 | siz := unsafe.Sizeof(raw) 41 | if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { 42 | return nil, err 43 | } 44 | 45 | addr := make([]byte, 1+net.IPv4len+2) 46 | addr[0] = socks5.AtypIPv4 47 | copy(addr[1:1+net.IPv4len], raw.Addr[:]) 48 | port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian 49 | addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] 50 | return addr, nil 51 | } 52 | -------------------------------------------------------------------------------- /proxy/redir/tcp_linux_386.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | const GETSOCKOPT = 15 // https://golang.org/src/syscall/syscall_linux_386.go#L183 9 | 10 | func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { 11 | var a [6]uintptr 12 | a[0], a[1], a[2], a[3], a[4], a[5] = a0, a1, a2, a3, a4, a5 13 | if _, _, errno := syscall.Syscall6(syscall.SYS_SOCKETCALL, call, uintptr(unsafe.Pointer(&a)), 0, 0, 0, 0); errno != 0 { 14 | return errno 15 | } 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /proxy/redir/tcp_linux_other.go: -------------------------------------------------------------------------------- 1 | // +build linux,!386 2 | 3 | package redir 4 | 5 | import "syscall" 6 | 7 | const GETSOCKOPT = syscall.SYS_GETSOCKOPT 8 | 9 | func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { 10 | if _, _, errno := syscall.Syscall6(call, a0, a1, a2, a3, a4, a5); errno != 0 { 11 | return errno 12 | } 13 | return nil 14 | } 15 | -------------------------------------------------------------------------------- /proxy/redir/tcp_windows.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | 7 | "github.com/zu1k/clashr/component/socks5" 8 | ) 9 | 10 | func parserPacket(conn net.Conn) (socks5.Addr, error) { 11 | return nil, errors.New("Windows not support yet") 12 | } 13 | -------------------------------------------------------------------------------- /proxy/socks/tcp.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "net" 7 | 8 | adapters "github.com/zu1k/clashr/adapters/inbound" 9 | "github.com/zu1k/clashr/component/socks5" 10 | C "github.com/zu1k/clashr/constant" 11 | "github.com/zu1k/clashr/log" 12 | authStore "github.com/zu1k/clashr/proxy/auth" 13 | "github.com/zu1k/clashr/tunnel" 14 | ) 15 | 16 | var ( 17 | tun = tunnel.Instance() 18 | ) 19 | 20 | type SockListener struct { 21 | net.Listener 22 | address string 23 | closed bool 24 | } 25 | 26 | func NewSocksProxy(addr string) (*SockListener, error) { 27 | l, err := net.Listen("tcp", addr) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | sl := &SockListener{l, addr, false} 33 | go func() { 34 | log.Infoln("SOCKS proxy listening at: %s", addr) 35 | for { 36 | c, err := l.Accept() 37 | if err != nil { 38 | if sl.closed { 39 | break 40 | } 41 | continue 42 | } 43 | go handleSocks(c) 44 | } 45 | }() 46 | 47 | return sl, nil 48 | } 49 | 50 | func (l *SockListener) Close() { 51 | l.closed = true 52 | _ = l.Listener.Close() 53 | } 54 | 55 | func (l *SockListener) Address() string { 56 | return l.address 57 | } 58 | 59 | func handleSocks(conn net.Conn) { 60 | target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) 61 | if err != nil { 62 | _ = conn.Close() 63 | return 64 | } 65 | _ = conn.(*net.TCPConn).SetKeepAlive(true) 66 | if command == socks5.CmdUDPAssociate { 67 | defer conn.Close() 68 | _, _ = io.Copy(ioutil.Discard, conn) 69 | return 70 | } 71 | tun.Add(adapters.NewSocket(target, conn, C.SOCKS, C.TCP)) 72 | } 73 | -------------------------------------------------------------------------------- /proxy/socks/udp.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | 7 | adapters "github.com/zu1k/clashr/adapters/inbound" 8 | "github.com/zu1k/clashr/common/pool" 9 | "github.com/zu1k/clashr/component/socks5" 10 | C "github.com/zu1k/clashr/constant" 11 | ) 12 | 13 | type SockUDPListener struct { 14 | net.PacketConn 15 | address string 16 | closed bool 17 | } 18 | 19 | func NewSocksUDPProxy(addr string) (*SockUDPListener, error) { 20 | l, err := net.ListenPacket("udp", addr) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | sl := &SockUDPListener{l, addr, false} 26 | go func() { 27 | for { 28 | buf := pool.BufPool.Get().([]byte) 29 | n, remoteAddr, err := l.ReadFrom(buf) 30 | if err != nil { 31 | pool.BufPool.Put(buf[:cap(buf)]) 32 | if sl.closed { 33 | break 34 | } 35 | continue 36 | } 37 | handleSocksUDP(l, buf[:n], remoteAddr) 38 | } 39 | }() 40 | 41 | return sl, nil 42 | } 43 | 44 | func (l *SockUDPListener) Close() error { 45 | l.closed = true 46 | return l.PacketConn.Close() 47 | } 48 | 49 | func (l *SockUDPListener) Address() string { 50 | return l.address 51 | } 52 | 53 | func handleSocksUDP(pc net.PacketConn, buf []byte, addr net.Addr) { 54 | target, payload, err := socks5.DecodeUDPPacket(buf) 55 | if err != nil { 56 | // Unresolved UDP packet, return buffer to the pool 57 | pool.BufPool.Put(buf[:cap(buf)]) 58 | return 59 | } 60 | conn := &fakeConn{ 61 | PacketConn: pc, 62 | remoteAddr: addr, 63 | targetAddr: target, 64 | buffer: bytes.NewBuffer(payload), 65 | bufRef: buf, 66 | } 67 | tun.Add(adapters.NewSocket(target, conn, C.SOCKS, C.UDP)) 68 | } 69 | -------------------------------------------------------------------------------- /proxy/socks/utils.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | 7 | "github.com/zu1k/clashr/common/pool" 8 | "github.com/zu1k/clashr/component/socks5" 9 | ) 10 | 11 | type fakeConn struct { 12 | net.PacketConn 13 | remoteAddr net.Addr 14 | targetAddr socks5.Addr 15 | buffer *bytes.Buffer 16 | bufRef []byte 17 | } 18 | 19 | func (c *fakeConn) Read(b []byte) (n int, err error) { 20 | return c.buffer.Read(b) 21 | } 22 | 23 | func (c *fakeConn) Write(b []byte) (n int, err error) { 24 | packet, err := socks5.EncodeUDPPacket(c.targetAddr, b) 25 | if err != nil { 26 | return 27 | } 28 | return c.PacketConn.WriteTo(packet, c.remoteAddr) 29 | } 30 | 31 | func (c *fakeConn) RemoteAddr() net.Addr { 32 | return c.remoteAddr 33 | } 34 | 35 | func (c *fakeConn) Close() error { 36 | err := c.PacketConn.Close() 37 | pool.BufPool.Put(c.bufRef[:cap(c.bufRef)]) 38 | return err 39 | } 40 | -------------------------------------------------------------------------------- /rules/domain.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | type Domain struct { 10 | domain string 11 | adapter string 12 | } 13 | 14 | func (d *Domain) RuleType() C.RuleType { 15 | return C.Domain 16 | } 17 | 18 | func (d *Domain) IsMatch(metadata *C.Metadata) bool { 19 | if metadata.AddrType != C.AtypDomainName { 20 | return false 21 | } 22 | return metadata.Host == d.domain 23 | } 24 | 25 | func (d *Domain) Adapter() string { 26 | return d.adapter 27 | } 28 | 29 | func (d *Domain) Payload() string { 30 | return d.domain 31 | } 32 | 33 | func NewDomain(domain string, adapter string) *Domain { 34 | return &Domain{ 35 | domain: strings.ToLower(domain), 36 | adapter: adapter, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rules/domain_keyword.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | type DomainKeyword struct { 10 | keyword string 11 | adapter string 12 | } 13 | 14 | func (dk *DomainKeyword) RuleType() C.RuleType { 15 | return C.DomainKeyword 16 | } 17 | 18 | func (dk *DomainKeyword) IsMatch(metadata *C.Metadata) bool { 19 | if metadata.AddrType != C.AtypDomainName { 20 | return false 21 | } 22 | domain := metadata.Host 23 | return strings.Contains(domain, dk.keyword) 24 | } 25 | 26 | func (dk *DomainKeyword) Adapter() string { 27 | return dk.adapter 28 | } 29 | 30 | func (dk *DomainKeyword) Payload() string { 31 | return dk.keyword 32 | } 33 | 34 | func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { 35 | return &DomainKeyword{ 36 | keyword: strings.ToLower(keyword), 37 | adapter: adapter, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rules/domain_suffix.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | type DomainSuffix struct { 10 | suffix string 11 | adapter string 12 | } 13 | 14 | func (ds *DomainSuffix) RuleType() C.RuleType { 15 | return C.DomainSuffix 16 | } 17 | 18 | func (ds *DomainSuffix) IsMatch(metadata *C.Metadata) bool { 19 | if metadata.AddrType != C.AtypDomainName { 20 | return false 21 | } 22 | domain := metadata.Host 23 | return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix 24 | } 25 | 26 | func (ds *DomainSuffix) Adapter() string { 27 | return ds.adapter 28 | } 29 | 30 | func (ds *DomainSuffix) Payload() string { 31 | return ds.suffix 32 | } 33 | 34 | func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { 35 | return &DomainSuffix{ 36 | suffix: strings.ToLower(suffix), 37 | adapter: adapter, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rules/final.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | C "github.com/zu1k/clashr/constant" 5 | ) 6 | 7 | type Match struct { 8 | adapter string 9 | } 10 | 11 | func (f *Match) RuleType() C.RuleType { 12 | return C.MATCH 13 | } 14 | 15 | func (f *Match) IsMatch(metadata *C.Metadata) bool { 16 | return true 17 | } 18 | 19 | func (f *Match) Adapter() string { 20 | return f.adapter 21 | } 22 | 23 | func (f *Match) Payload() string { 24 | return "" 25 | } 26 | 27 | func NewMatch(adapter string) *Match { 28 | return &Match{ 29 | adapter: adapter, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rules/geoip.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "sync" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | 8 | "github.com/oschwald/geoip2-golang" 9 | log "github.com/sirupsen/logrus" 10 | ) 11 | 12 | var ( 13 | mmdb *geoip2.Reader 14 | once sync.Once 15 | ) 16 | 17 | type GEOIP struct { 18 | country string 19 | adapter string 20 | } 21 | 22 | func (g *GEOIP) RuleType() C.RuleType { 23 | return C.GEOIP 24 | } 25 | 26 | func (g *GEOIP) IsMatch(metadata *C.Metadata) bool { 27 | if metadata.DstIP == nil { 28 | return false 29 | } 30 | record, _ := mmdb.Country(*metadata.DstIP) 31 | return record.Country.IsoCode == g.country 32 | } 33 | 34 | func (g *GEOIP) Adapter() string { 35 | return g.adapter 36 | } 37 | 38 | func (g *GEOIP) Payload() string { 39 | return g.country 40 | } 41 | 42 | func NewGEOIP(country string, adapter string) *GEOIP { 43 | once.Do(func() { 44 | var err error 45 | mmdb, err = geoip2.Open(C.Path.MMDB()) 46 | if err != nil { 47 | log.Fatalf("Can't load mmdb: %s", err.Error()) 48 | } 49 | }) 50 | return &GEOIP{ 51 | country: country, 52 | adapter: adapter, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rules/ipcidr.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "net" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | type IPCIDR struct { 10 | ipnet *net.IPNet 11 | adapter string 12 | isSourceIP bool 13 | } 14 | 15 | func (i *IPCIDR) RuleType() C.RuleType { 16 | if i.isSourceIP { 17 | return C.SrcIPCIDR 18 | } 19 | return C.IPCIDR 20 | } 21 | 22 | func (i *IPCIDR) IsMatch(metadata *C.Metadata) bool { 23 | ip := metadata.DstIP 24 | if i.isSourceIP { 25 | ip = metadata.SrcIP 26 | } 27 | return ip != nil && i.ipnet.Contains(*ip) 28 | } 29 | 30 | func (i *IPCIDR) Adapter() string { 31 | return i.adapter 32 | } 33 | 34 | func (i *IPCIDR) Payload() string { 35 | return i.ipnet.String() 36 | } 37 | 38 | func NewIPCIDR(s string, adapter string, isSourceIP bool) *IPCIDR { 39 | _, ipnet, err := net.ParseCIDR(s) 40 | if err != nil { 41 | return nil 42 | } 43 | return &IPCIDR{ 44 | ipnet: ipnet, 45 | adapter: adapter, 46 | isSourceIP: isSourceIP, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rules/port.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "strconv" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | type Port struct { 10 | adapter string 11 | port string 12 | isSource bool 13 | } 14 | 15 | func (p *Port) RuleType() C.RuleType { 16 | if p.isSource { 17 | return C.SrcPort 18 | } 19 | return C.DstPort 20 | } 21 | 22 | func (p *Port) IsMatch(metadata *C.Metadata) bool { 23 | if p.isSource { 24 | return metadata.SrcPort == p.port 25 | } 26 | return metadata.DstPort == p.port 27 | } 28 | 29 | func (p *Port) Adapter() string { 30 | return p.adapter 31 | } 32 | 33 | func (p *Port) Payload() string { 34 | return p.port 35 | } 36 | 37 | func NewPort(port string, adapter string, isSource bool) *Port { 38 | _, err := strconv.Atoi(port) 39 | if err != nil { 40 | return nil 41 | } 42 | return &Port{ 43 | adapter: adapter, 44 | port: port, 45 | isSource: isSource, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tunnel/connection.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "net" 7 | "net/http" 8 | "strings" 9 | "time" 10 | 11 | adapters "github.com/zu1k/clashr/adapters/inbound" 12 | "github.com/zu1k/clashr/common/pool" 13 | ) 14 | 15 | func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) { 16 | conn := newTrafficTrack(outbound, t.traffic) 17 | req := request.R 18 | host := req.Host 19 | 20 | inboundReeder := bufio.NewReader(request) 21 | outboundReeder := bufio.NewReader(conn) 22 | 23 | for { 24 | keepAlive := strings.TrimSpace(strings.ToLower(req.Header.Get("Proxy-Connection"))) == "keep-alive" 25 | 26 | req.Header.Set("Connection", "close") 27 | req.RequestURI = "" 28 | adapters.RemoveHopByHopHeaders(req.Header) 29 | err := req.Write(conn) 30 | if err != nil { 31 | break 32 | } 33 | 34 | handleResponse: 35 | resp, err := http.ReadResponse(outboundReeder, req) 36 | if err != nil { 37 | break 38 | } 39 | adapters.RemoveHopByHopHeaders(resp.Header) 40 | 41 | if resp.StatusCode == http.StatusContinue { 42 | err = resp.Write(request) 43 | if err != nil { 44 | break 45 | } 46 | goto handleResponse 47 | } 48 | 49 | if keepAlive || resp.ContentLength >= 0 { 50 | resp.Header.Set("Proxy-Connection", "keep-alive") 51 | resp.Header.Set("Connection", "keep-alive") 52 | resp.Header.Set("Keep-Alive", "timeout=4") 53 | resp.Close = false 54 | } else { 55 | resp.Close = true 56 | } 57 | err = resp.Write(request) 58 | if err != nil || resp.Close { 59 | break 60 | } 61 | 62 | req, err = http.ReadRequest(inboundReeder) 63 | if err != nil { 64 | break 65 | } 66 | 67 | // Sometimes firefox just open a socket to process multiple domains in HTTP 68 | // The temporary solution is close connection when encountering different HOST 69 | if req.Host != host { 70 | break 71 | } 72 | } 73 | } 74 | 75 | func (t *Tunnel) handleUDPToRemote(conn net.Conn, pc net.PacketConn, addr net.Addr) { 76 | buf := pool.BufPool.Get().([]byte) 77 | defer pool.BufPool.Put(buf[:cap(buf)]) 78 | 79 | n, err := conn.Read(buf) 80 | if err != nil { 81 | return 82 | } 83 | if _, err = pc.WriteTo(buf[:n], addr); err != nil { 84 | return 85 | } 86 | t.traffic.Up() <- int64(n) 87 | } 88 | 89 | func (t *Tunnel) handleUDPToLocal(conn net.Conn, pc net.PacketConn, key string, timeout time.Duration) { 90 | buf := pool.BufPool.Get().([]byte) 91 | defer pool.BufPool.Put(buf[:cap(buf)]) 92 | defer t.natTable.Delete(key) 93 | defer pc.Close() 94 | 95 | for { 96 | pc.SetReadDeadline(time.Now().Add(timeout)) 97 | n, _, err := pc.ReadFrom(buf) 98 | if err != nil { 99 | return 100 | } 101 | 102 | n, err = conn.Write(buf[:n]) 103 | if err != nil { 104 | return 105 | } 106 | t.traffic.Down() <- int64(n) 107 | } 108 | } 109 | 110 | func (t *Tunnel) handleSocket(request *adapters.SocketAdapter, outbound net.Conn) { 111 | conn := newTrafficTrack(outbound, t.traffic) 112 | relay(request, conn) 113 | } 114 | 115 | // relay copies between left and right bidirectionally. 116 | func relay(leftConn, rightConn net.Conn) { 117 | ch := make(chan error) 118 | 119 | go func() { 120 | buf := pool.BufPool.Get().([]byte) 121 | _, err := io.CopyBuffer(leftConn, rightConn, buf) 122 | pool.BufPool.Put(buf[:cap(buf)]) 123 | leftConn.SetReadDeadline(time.Now()) 124 | ch <- err 125 | }() 126 | 127 | buf := pool.BufPool.Get().([]byte) 128 | io.CopyBuffer(rightConn, leftConn, buf) 129 | pool.BufPool.Put(buf[:cap(buf)]) 130 | rightConn.SetReadDeadline(time.Now()) 131 | <-ch 132 | } 133 | -------------------------------------------------------------------------------- /tunnel/mode.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | type Mode int 9 | 10 | var ( 11 | // ModeMapping is a mapping for Mode enum 12 | ModeMapping = map[string]Mode{ 13 | Global.String(): Global, 14 | Rule.String(): Rule, 15 | Direct.String(): Direct, 16 | } 17 | ) 18 | 19 | const ( 20 | Global Mode = iota 21 | Rule 22 | Direct 23 | ) 24 | 25 | // UnmarshalJSON unserialize Mode 26 | func (m *Mode) UnmarshalJSON(data []byte) (err error) { 27 | var tp string 28 | err = json.Unmarshal(data, &tp) 29 | mode, exist := ModeMapping[tp] 30 | if !exist { 31 | return errors.New("invalid mode") 32 | } 33 | *m = mode 34 | return 35 | } 36 | 37 | // UnmarshalYAML unserialize Mode with yaml 38 | func (m *Mode) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { 39 | var tp string 40 | err = unmarshal(&tp) 41 | mode, exist := ModeMapping[tp] 42 | if !exist { 43 | return errors.New("invalid mode") 44 | } 45 | *m = mode 46 | return 47 | } 48 | 49 | // MarshalJSON serialize Mode 50 | func (m Mode) MarshalJSON() ([]byte, error) { 51 | return json.Marshal(m.String()) 52 | } 53 | 54 | func (m Mode) String() string { 55 | switch m { 56 | case Global: 57 | return "Global" 58 | case Rule: 59 | return "Rule" 60 | case Direct: 61 | return "Direct" 62 | default: 63 | return "Unknown" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tunnel/util.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "net" 5 | 6 | C "github.com/zu1k/clashr/constant" 7 | ) 8 | 9 | // TrafficTrack record traffic of net.Conn 10 | type TrafficTrack struct { 11 | net.Conn 12 | traffic *C.Traffic 13 | } 14 | 15 | func (tt *TrafficTrack) Read(b []byte) (int, error) { 16 | n, err := tt.Conn.Read(b) 17 | tt.traffic.Down() <- int64(n) 18 | return n, err 19 | } 20 | 21 | func (tt *TrafficTrack) Write(b []byte) (int, error) { 22 | n, err := tt.Conn.Write(b) 23 | tt.traffic.Up() <- int64(n) 24 | return n, err 25 | } 26 | 27 | func newTrafficTrack(conn net.Conn, traffic *C.Traffic) *TrafficTrack { 28 | return &TrafficTrack{traffic: traffic, Conn: conn} 29 | } 30 | --------------------------------------------------------------------------------