diff --git a/.dockerignore b/.dockerignore index 92ce733..b936491 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,3 @@ -.github/ -.github/** -assets/ -assets/** -.dockerignore -*.yml -*.md +.github .gitignore -Dockerfile* +.dockerignore diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b25c8b5..c7af07b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -3,13 +3,11 @@ name: Publish Docker Image on: push: branches: - - master + - dev tags: - '*' paths-ignore: - - '.github/**' - - 'assets/**' - - '.gitignore' + - 'docs/**' - 'README.md' jobs: @@ -40,8 +38,15 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: xjasonlyu + password: ${{ secrets.CR_PAT }} + - name: Build and Push (dev) - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/dev' uses: docker/build-push-action@v2 with: context: . @@ -49,6 +54,7 @@ jobs: platforms: linux/amd64,linux/arm64 tags: | xjasonlyu/tun2socks:dev + ghcr.io/xjasonlyu/tun2socks:dev - name: Get Version id: shell @@ -65,3 +71,5 @@ jobs: tags: | xjasonlyu/tun2socks:latest xjasonlyu/tun2socks:${{ steps.shell.outputs.version }} + ghcr.io/xjasonlyu/tun2socks:latest + ghcr.io/xjasonlyu/tun2socks:${{ steps.shell.outputs.version }} diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b788341..8c619f3 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,19 +1,13 @@ name: Go on: + pull_request: push: branches: - - master - tags: - - '*' - paths-ignore: - - '.github/**' - - 'assets/**' - - '.gitignore' - - 'README.md' + - main + - dev jobs: - build: name: Build runs-on: ubuntu-latest diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..d0f405c --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,16 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "0 10 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' + days-before-stale: 60 + days-before-close: 7 diff --git a/.gitignore b/.gitignore index df10585..14712fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,14 @@ - -.idea/ -.vscode/ +# Binaries +*.exe +*.exe~ +*.dll +*.so +*.dylib bin/ +# IDE +.idea/ +.vscode/ + +# Misc .DS_Store diff --git a/Dockerfile b/Dockerfile index 00a3fdf..0eb472c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,13 +9,14 @@ RUN apk add --no-cache make git \ && mv ./bin/tun2socks-docker /tun2socks FROM alpine:latest - -COPY ./scripts/entrypoint.sh /entrypoint.sh -COPY --from=builder /tun2socks /usr/bin/tun2socks +LABEL org.opencontainers.image.source="https://github.com/xjasonlyu/tun2socks" RUN apk add --update --no-cache iptables iproute2 \ && chmod +x /entrypoint.sh +COPY docker/entrypoint.sh /entrypoint.sh +COPY --from=builder /tun2socks /usr/bin/tun2socks + ENV TUN tun0 ENV ETH eth0 ENV TUN_ADDR= @@ -23,10 +24,8 @@ ENV TUN_MASK= ENV LOGLEVEL= ENV EXCLUDED= ENV EXTRACMD= - -ENV API= -ENV DNS= -ENV HOSTS= ENV PROXY= +ENV STATS= +ENV SECRET= ENTRYPOINT ["/entrypoint.sh"] diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 deleted file mode 100644 index 6728189..0000000 --- a/Dockerfile.aarch64 +++ /dev/null @@ -1,32 +0,0 @@ -FROM arm64v8/golang:alpine AS builder - -WORKDIR /app -COPY . /app - -RUN apk add --no-cache make git \ - && go mod download \ - && make docker \ - && /app/bin/tun2socks-docker -v - -FROM arm64v8/alpine:latest - -COPY ./scripts/entrypoint.sh /entrypoint.sh -COPY --from=builder /app/bin/tun2socks-docker /usr/bin/tun2socks - -RUN apk add --update --no-cache iptables iproute2 \ - && chmod +x /entrypoint.sh - -ENV TUN tun0 -ENV ETH eth0 -ENV TUN_ADDR= -ENV TUN_MASK= -ENV LOGLEVEL= -ENV EXCLUDED= -ENV EXTRACMD= - -ENV API= -ENV DNS= -ENV HOSTS= -ENV PROXY= - -ENTRYPOINT ["/entrypoint.sh"] diff --git a/Makefile b/Makefile index 77a4c02..4ace8e7 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -DIR = "bin" +BINDIR = "bin" NAME = "tun2socks" TAGS = "" @@ -8,9 +8,9 @@ VERSION = $(shell git describe --tags || echo "unknown version") BUILD_TIME = $(shell date -u '+%FT%TZ') LDFLAGS += -w -s -buildid= -LDFLAGS += -X "main.Version=$(VERSION)" -LDFLAGS += -X "main.BuildTime=$(BUILD_TIME)" # RFC3339 -GO_BUILD = CGO_ENABLED=0 go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)' -trimpath +LDFLAGS += -X "github.com/xjasonlyu/tun2socks/constant.Version=$(VERSION)" +LDFLAGS += -X "github.com/xjasonlyu/tun2socks/constant.BuildTime=$(BUILD_TIME)" # RFC3339 +GO_BUILD = GO111MODULE=on CGO_ENABLED=0 go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)' -trimpath PLATFORM_LIST = \ darwin-amd64 \ @@ -21,40 +21,52 @@ PLATFORM_LIST = \ openbsd-amd64 \ openbsd-arm64 \ -all: linux-amd64 darwin-amd64 +WINDOWS_ARCH_LIST = \ + windows-amd64 \ +all: linux-amd64 darwin-amd64 windows-amd64 + +.PHONY: docker docker: - $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ darwin-amd64: - GOARCH=amd64 GOOS=darwin $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=amd64 GOOS=darwin $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ freebsd-amd64: - GOARCH=amd64 GOOS=freebsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=amd64 GOOS=freebsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ freebsd-arm64: - GOARCH=arm64 GOOS=freebsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=arm64 GOOS=freebsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ linux-amd64: - GOARCH=amd64 GOOS=linux $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=amd64 GOOS=linux $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ linux-arm64: - GOARCH=arm64 GOOS=linux $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=arm64 GOOS=linux $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ openbsd-amd64: - GOARCH=amd64 GOOS=openbsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=amd64 GOOS=openbsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ openbsd-arm64: - GOARCH=arm64 GOOS=openbsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@ + GOARCH=arm64 GOOS=openbsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@ -zip_releases=$(addsuffix .zip, $(PLATFORM_LIST)) +windows-amd64: + GOARCH=amd64 GOOS=windows $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@.exe + +gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) +zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST)) + +$(gz_releases): %.gz : % + chmod +x $(BINDIR)/$(NAME)-$(basename $@) + gzip -f -S .gz $(BINDIR)/$(NAME)-$(basename $@) $(zip_releases): %.zip : % - zip -m -j $(DIR)/$(NAME)-$(basename $@).zip $(DIR)/$(NAME)-$(basename $@) + zip -m -j $(BINDIR)/$(NAME)-$(basename $@).zip $(BINDIR)/$(NAME)-$(basename $@).exe -all-arch: $(PLATFORM_LIST) +all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) -releases: $(zip_releases) +releases: $(gz_releases) $(zip_releases) clean: - rm $(DIR)/* + rm $(BINDIR)/* diff --git a/README.md b/README.md index 96f0db5..23a6671 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,6 @@ > iPerf3 tested on Debian 10 with i5-10500, 8G RAM -![iPerf3 Test](assets/iperf3.png) - ## How to Build ### build from source code @@ -250,22 +248,15 @@ PROXY=socks5://server:port LOGLEVEL=INFO sh ./scripts/entrypoint.sh Help Text ```text -NAME: - tun2socks - A tun2socks powered by gVisor TCP/IP stack. - -USAGE: - tun2socks [global options] [arguments...] - -GLOBAL OPTIONS: - --api value URL of external API to listen - --device value, -d value URL of device to open - --dns value URL of fake DNS to listen - --hosts value Extra hosts mapping - --interface value, -i value Bind interface to dial - --loglevel value, -l value Set logging level (default: "INFO") - --proxy value, -p value URL of proxy to dial - --version, -v Print current version (default: false) - --help, -h show help (default: false) +Usage of tun2socks: + -d, --device string Use this device [driver://]name + -i, --interface string Use network INTERFACE (Darwin/Linux only) + -l, --loglevel string Log level [debug|info|warn|error|silent] (default "info") + -m, --mtu int Maximum transmission unit + -p, --proxy string Use this proxy [protocol://]host[:port] + --secret string HTTP statistic server auth secret + --stats string HTTP statistic server listen address + -v, --version Show version information and quit ``` @@ -295,6 +286,6 @@ If you are sensitive to memory, please go back to [v1](https://github.com/xjason ## TODO -- [ ] Windows support +- [x] Windows support - [x] FreeBSD support - [x] OpenBSD support diff --git a/assets/iperf3.png b/assets/iperf3.png deleted file mode 100644 index 48a5b10..0000000 Binary files a/assets/iperf3.png and /dev/null differ diff --git a/internal/adapter/adapter.go b/common/adapter/adapter.go old mode 100644 new mode 100755 similarity index 86% rename from internal/adapter/adapter.go rename to common/adapter/adapter.go index e1fe0d6..4880cc5 --- a/internal/adapter/adapter.go +++ b/common/adapter/adapter.go @@ -1,6 +1,8 @@ package adapter -import "net" +import ( + "net" +) type TCPConn interface { net.Conn @@ -25,7 +27,6 @@ type UDPPacket interface { // WriteBack writes the payload with source IP/Port equals addr // - variable source IP/Port is important to STUN - // - if addr is not provided, WriteBack will write out UDP packet with SourceIP/Port equals to original Target, - // this is important when using Fake-IP. + // - if addr is not provided, WriteBack will write out UDP packet with SourceIP/Port equals to original Target. WriteBack([]byte, net.Addr) (int, error) } diff --git a/internal/adapter/metadata.go b/common/adapter/metadata.go old mode 100644 new mode 100755 similarity index 54% rename from internal/adapter/metadata.go rename to common/adapter/metadata.go index b509e1f..cca8d8b --- a/internal/adapter/metadata.go +++ b/common/adapter/metadata.go @@ -2,11 +2,12 @@ package adapter import ( "bytes" - "encoding/json" + "encoding/binary" + "fmt" "net" "strconv" - "github.com/xjasonlyu/clash/component/socks5" + "github.com/xjasonlyu/tun2socks/component/socks5" ) const ( @@ -14,17 +15,21 @@ const ( UDP ) -type Network int +type Network uint8 func (n Network) String() string { - if n == TCP { + switch n { + case TCP: return "tcp" + case UDP: + return "udp" + default: + return fmt.Sprintf("network(%d)", n) } - return "udp" } -func (n Network) MarshalJSON() ([]byte, error) { - return json.Marshal(n.String()) +func (n Network) MarshalText() ([]byte, error) { + return []byte(n.String()), nil } // Metadata implements the net.Addr interface. @@ -36,16 +41,10 @@ type Metadata struct { SrcPort uint16 `json:"sourcePort"` MidPort uint16 `json:"dialerPort"` DstPort uint16 `json:"destinationPort"` - Host string `json:"host"` } func (m *Metadata) DestinationAddress() string { - if m.Host != "" { - return net.JoinHostPort(m.Host, strconv.FormatUint(uint64(m.DstPort), 10)) - } else if m.DstIP != nil { - return net.JoinHostPort(m.DstIP.String(), strconv.FormatUint(uint64(m.DstPort), 10)) - } - return "" + return net.JoinHostPort(m.DstIP.String(), strconv.FormatUint(uint64(m.DstPort), 10)) } func (m *Metadata) SourceAddress() string { @@ -63,22 +62,18 @@ func (m *Metadata) UDPAddr() *net.UDPAddr { } func (m *Metadata) SerializesSocksAddr() socks5.Addr { - var buf [][]byte - port := []byte{uint8(m.DstPort >> 8), uint8(m.DstPort & 0xff)} - switch { - case m.Host != "": /* Domain */ - aType := socks5.AtypDomainName - length := uint8(len(m.Host)) - host := []byte(m.Host) - buf = [][]byte{{uint8(aType), length}, host, port} - case m.DstIP.To4() != nil: /* IPv4 */ + var ( + buf [][]byte + port [2]byte + ) + binary.BigEndian.PutUint16(port[:], m.DstPort) + + if m.DstIP.To4() != nil /* IPv4 */ { aType := socks5.AtypIPv4 - host := m.DstIP.To4() - buf = [][]byte{{uint8(aType)}, host, port} - default: /* IPv6 */ + buf = [][]byte{{aType}, m.DstIP.To4(), port[:]} + } else /* IPv6 */ { aType := socks5.AtypIPv6 - host := m.DstIP.To16() - buf = [][]byte{{uint8(aType)}, host, port} + buf = [][]byte{{aType}, m.DstIP.To16(), port[:]} } return bytes.Join(buf, nil) } @@ -87,10 +82,12 @@ func (m *Metadata) Network() string { return m.Net.String() } +// String returns destination address of this metadata. +// Also for implementing net.Addr interface. func (m *Metadata) String() string { return m.DestinationAddress() } func (m *Metadata) Valid() bool { - return m.Host != "" || m.DstIP != nil + return m.SrcIP != nil && m.DstIP != nil } diff --git a/common/observable/iterable.go b/common/observable/iterable.go new file mode 100755 index 0000000..ad0943b --- /dev/null +++ b/common/observable/iterable.go @@ -0,0 +1,3 @@ +package observable + +type Iterable <-chan interface{} diff --git a/common/observable/observable.go b/common/observable/observable.go new file mode 100755 index 0000000..bf0a1e9 --- /dev/null +++ b/common/observable/observable.go @@ -0,0 +1,67 @@ +package observable + +// Ref: github.com/Dreamacro/clash/common/observable + +import ( + "errors" + "sync" +) + +type Observable struct { + iterable Iterable + listener map[Subscription]*Subscriber + mux sync.Mutex + done bool +} + +func (o *Observable) process() { + for item := range o.iterable { + o.mux.Lock() + for _, sub := range o.listener { + sub.Emit(item) + } + o.mux.Unlock() + } + o.close() +} + +func (o *Observable) close() { + o.mux.Lock() + defer o.mux.Unlock() + + o.done = true + for _, sub := range o.listener { + sub.Close() + } +} + +func (o *Observable) Subscribe() (Subscription, error) { + o.mux.Lock() + defer o.mux.Unlock() + if o.done { + return nil, errors.New("observable is closed") + } + subscriber := newSubscriber() + o.listener[subscriber.Out()] = subscriber + return subscriber.Out(), nil +} + +func (o *Observable) UnSubscribe(sub Subscription) { + o.mux.Lock() + defer o.mux.Unlock() + subscriber, exist := o.listener[sub] + if !exist { + return + } + delete(o.listener, sub) + subscriber.Close() +} + +func NewObservable(any Iterable) *Observable { + observable := &Observable{ + iterable: any, + listener: map[Subscription]*Subscriber{}, + } + go observable.process() + return observable +} diff --git a/common/observable/observable_test.go b/common/observable/observable_test.go new file mode 100755 index 0000000..5a9e6d3 --- /dev/null +++ b/common/observable/observable_test.go @@ -0,0 +1,148 @@ +package observable + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/atomic" +) + +func iterator(item []interface{}) chan interface{} { + ch := make(chan interface{}) + go func() { + time.Sleep(100 * time.Millisecond) + for _, elm := range item { + ch <- elm + } + close(ch) + }() + return ch +} + +func TestObservable(t *testing.T) { + iter := iterator([]interface{}{1, 2, 3, 4, 5}) + src := NewObservable(iter) + data, err := src.Subscribe() + assert.Nil(t, err) + count := 0 + for range data { + count++ + } + assert.Equal(t, count, 5) +} + +func TestObservable_MultiSubscribe(t *testing.T) { + iter := iterator([]interface{}{1, 2, 3, 4, 5}) + src := NewObservable(iter) + ch1, _ := src.Subscribe() + ch2, _ := src.Subscribe() + var count = atomic.NewInt32(0) + + var wg sync.WaitGroup + wg.Add(2) + waitCh := func(ch <-chan interface{}) { + for range ch { + count.Inc() + } + wg.Done() + } + go waitCh(ch1) + go waitCh(ch2) + wg.Wait() + assert.Equal(t, int32(10), count.Load()) +} + +func TestObservable_UnSubscribe(t *testing.T) { + iter := iterator([]interface{}{1, 2, 3, 4, 5}) + src := NewObservable(iter) + data, err := src.Subscribe() + assert.Nil(t, err) + src.UnSubscribe(data) + _, open := <-data + assert.False(t, open) +} + +func TestObservable_SubscribeClosedSource(t *testing.T) { + iter := iterator([]interface{}{1}) + src := NewObservable(iter) + data, _ := src.Subscribe() + <-data + + _, closed := src.Subscribe() + assert.NotNil(t, closed) +} + +func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) { + sub := Subscription(make(chan interface{})) + iter := iterator([]interface{}{1}) + src := NewObservable(iter) + src.UnSubscribe(sub) +} + +func TestObservable_SubscribeGoroutineLeak(t *testing.T) { + iter := iterator([]interface{}{1, 2, 3, 4, 5}) + src := NewObservable(iter) + max := 100 + + var list []Subscription + for i := 0; i < max; i++ { + ch, _ := src.Subscribe() + list = append(list, ch) + } + + var wg sync.WaitGroup + wg.Add(max) + waitCh := func(ch <-chan interface{}) { + for range ch { + } + wg.Done() + } + + for _, ch := range list { + go waitCh(ch) + } + wg.Wait() + + for _, sub := range list { + _, more := <-sub + assert.False(t, more) + } + + if len(list) > 0 { + _, more := <-list[0] + assert.False(t, more) + } +} + +func Benchmark_Observable_1000(b *testing.B) { + ch := make(chan interface{}) + o := NewObservable(ch) + num := 1000 + + var subs []Subscription + for i := 0; i < num; i++ { + sub, _ := o.Subscribe() + subs = append(subs, sub) + } + + wg := sync.WaitGroup{} + wg.Add(num) + + b.ResetTimer() + for _, sub := range subs { + go func(s Subscription) { + for range s { + } + wg.Done() + }(sub) + } + + for i := 0; i < b.N; i++ { + ch <- i + } + + close(ch) + wg.Wait() +} diff --git a/common/observable/subscriber.go b/common/observable/subscriber.go new file mode 100755 index 0000000..cb2a70f --- /dev/null +++ b/common/observable/subscriber.go @@ -0,0 +1,33 @@ +package observable + +import ( + "sync" +) + +type Subscription <-chan interface{} + +type Subscriber struct { + buffer chan interface{} + once sync.Once +} + +func (s *Subscriber) Emit(item interface{}) { + s.buffer <- item +} + +func (s *Subscriber) Out() Subscription { + return s.buffer +} + +func (s *Subscriber) Close() { + s.once.Do(func() { + close(s.buffer) + }) +} + +func newSubscriber() *Subscriber { + sub := &Subscriber{ + buffer: make(chan interface{}, 200), + } + return sub +} diff --git a/common/pool/alloc.go b/common/pool/alloc.go new file mode 100755 index 0000000..1f0a912 --- /dev/null +++ b/common/pool/alloc.go @@ -0,0 +1,62 @@ +package pool + +import ( + "errors" + "math/bits" + "sync" +) + +var _allocator = NewAllocator() + +// Allocator for incoming frames, optimized to prevent overwriting +// after zeroing. +type Allocator struct { + buffers []sync.Pool +} + +// NewAllocator initiates a []byte allocator for frames less than +// 65536 bytes, the waste(memory fragmentation) of space allocation +// is guaranteed to be no more than 50%. +func NewAllocator() *Allocator { + alloc := &Allocator{} + alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K + for k := range alloc.buffers { + i := k + alloc.buffers[k].New = func() interface{} { + return make([]byte, 1< 65536 { + return nil + } + + b := msb(size) + if size == 1< 65536 || cap(buf) != 1< MaxAuthLen { + return nil, errors.New("auth message too long") + } + + if _, err := rw.Write(authMsg.Bytes()); err != nil { + return nil, err + } + + if _, err := io.ReadFull(rw, buf[:2]); err != nil { + return nil, err + } + + if buf[1] != 0x00 /* STATUS of SUCCESS */ { + return nil, errors.New("rejected username/password") + } + + } else if buf[1] != 0x00 /* NO AUTHENTICATION REQUIRED */ { + return nil, errors.New("unsupported method") + } + + // VER, CMD, RSV, ADDR + if _, err := rw.Write(bytes.Join([][]byte{{Version, command, 0x00 /* RSV */}, addr}, nil)); err != nil { + return nil, err + } + + // VER, REP, RSV + if _, err := io.ReadFull(rw, buf[:3]); err != nil { + return nil, err + } + + if buf[1] != 0x00 /* SUCCEEDED */ { + return nil, fmt.Errorf("unsucceed reply: %X", buf[1]) + } + + return ReadAddr(rw, buf) +} + +func ReadAddr(r io.Reader, b []byte) (Addr, error) { + if len(b) < MaxAddrLen { + return nil, io.ErrShortBuffer + } + + // read 1st byte for address type + if _, err := io.ReadFull(r, b[:1]); err != nil { + return nil, err + } + + switch b[0] /* ATYP */ { + case AtypDomainName: + // read 2nd byte for domain length + if _, err := io.ReadFull(r, b[1:2]); err != nil { + return nil, err + } + domainLength := uint16(b[1]) + _, err := io.ReadFull(r, b[2:2+domainLength+2]) + return b[:1+1+domainLength+2], err + case AtypIPv4: + _, err := io.ReadFull(r, b[1:1+net.IPv4len+2]) + return b[:1+net.IPv4len+2], err + case AtypIPv6: + _, err := io.ReadFull(r, b[1:1+net.IPv6len+2]) + return b[:1+net.IPv6len+2], err + default: + return nil, errors.New("invalid address type") + } +} + +// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed. +func SplitAddr(b []byte) Addr { + addrLen := 1 + if len(b) < addrLen { + return nil + } + + switch b[0] { + case AtypDomainName: + if len(b) < 2 { + return nil + } + addrLen = 1 + 1 + int(b[1]) + 2 + case AtypIPv4: + addrLen = 1 + net.IPv4len + 2 + case AtypIPv6: + addrLen = 1 + net.IPv6len + 2 + default: + return nil + } + + if len(b) < addrLen { + return nil + } + + return b[:addrLen] +} + +// ParseAddr parses the address in string s. Returns nil if failed. +func ParseAddr(s string) Addr { + host, port, err := net.SplitHostPort(s) + if err != nil { + return nil + } + + var addr Addr + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + addr = make([]byte, 1+net.IPv4len+2) + addr[0] = AtypIPv4 + copy(addr[1:], ip4) + } else { + addr = make([]byte, 1+net.IPv6len+2) + addr[0] = AtypIPv6 + copy(addr[1:], ip) + } + } else { + if len(host) > 255 { + return nil + } + addr = make([]byte, 1+1+len(host)+2) + addr[0] = AtypDomainName + addr[1] = byte(len(host)) + copy(addr[2:], host) + } + + p, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return nil + } + binary.BigEndian.PutUint16(addr[len(addr)-2:], uint16(p)) + + return addr +} + +// ParseAddrToSocksAddr parse a socks addr from net.addr +// This is a fast path of ParseAddr(addr.String()) +func ParseAddrToSocksAddr(addr net.Addr) Addr { + var ip net.IP + var port int + if udpAddr, ok := addr.(*net.UDPAddr); ok { + ip = udpAddr.IP + port = udpAddr.Port + } else if tcpAddr, ok := addr.(*net.TCPAddr); ok { + ip = tcpAddr.IP + port = tcpAddr.Port + } + + // fallback parse + if ip == nil { + return ParseAddr(addr.String()) + } + + var parsed Addr + if ip4 := ip.To4(); ip4 != nil { + parsed = make([]byte, 1+net.IPv4len+2) + parsed[0] = AtypIPv4 + copy(parsed[1:], ip4) + binary.BigEndian.PutUint16(parsed[1+net.IPv4len:], uint16(port)) + } else { + parsed = make([]byte, 1+net.IPv6len+2) + parsed[0] = AtypIPv6 + copy(parsed[1:], ip) + binary.BigEndian.PutUint16(parsed[1+net.IPv6len:], uint16(port)) + } + return parsed +} + +// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet` +func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) { + if len(packet) < 5 { + err = errors.New("insufficient length of packet") + return + } + + // packet[0] and packet[1] are reserved + if !bytes.Equal(packet[:2], []byte{0x00, 0x00}) { + err = errors.New("reserved fields should be zero") + return + } + + if packet[2] != 0x00 /* fragments */ { + err = errors.New("discarding fragmented payload") + return + } + + addr = SplitAddr(packet[3:]) + if addr == nil { + err = errors.New("socks5 UDP addr is nil") + } + + payload = packet[3+len(addr):] + return +} + +func EncodeUDPPacket(addr Addr, payload []byte) (packet []byte, err error) { + if addr == nil { + return nil, errors.New("address is invalid") + } + packet = bytes.Join([][]byte{{0x00, 0x00, 0x00}, addr, payload}, nil) + return +} diff --git a/constant/constant.go b/constant/constant.go new file mode 100644 index 0000000..66ab104 --- /dev/null +++ b/constant/constant.go @@ -0,0 +1,2 @@ +// Package constant provides global constants/variables of this project. +package constant diff --git a/constant/version.go b/constant/version.go new file mode 100755 index 0000000..c34a6fd --- /dev/null +++ b/constant/version.go @@ -0,0 +1,8 @@ +package constant + +const Name = "tun2socks" + +var ( + Version = "" + BuildTime = "" +) diff --git a/device/device.go b/device/device.go new file mode 100755 index 0000000..8b0fe1b --- /dev/null +++ b/device/device.go @@ -0,0 +1,17 @@ +package device + +import ( + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +// Device is the interface that implemented by network layer devices (e.g. tun), +// and easy to use as stack.LinkEndpoint. +type Device interface { + stack.LinkEndpoint + + // Close stops and closes the device. + Close() error + + // Name returns the current name of the device. + Name() string +} diff --git a/pkg/link/rwc/endpoint.go b/device/rwbased/endpoint.go old mode 100644 new mode 100755 similarity index 68% rename from pkg/link/rwc/endpoint.go rename to device/rwbased/endpoint.go index 772131b..f8e25c6 --- a/pkg/link/rwc/endpoint.go +++ b/device/rwbased/endpoint.go @@ -1,9 +1,10 @@ -package rwc +// Package channel provides the implementation of io.ReadWriter +// based data-link layer endpoints. +package rwbased import ( "errors" "io" - "sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/buffer" @@ -11,28 +12,34 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/stack" ) -// Endpoint wraps io.ReadWriter to stack.LinkEndpoint. +var _ stack.LinkEndpoint = (*Endpoint)(nil) + +// Endpoint implements the interface of stack.LinkEndpoint from io.ReadWriter. type Endpoint struct { + // rw is the io.ReadWriter for reading and writing packets. + rw io.ReadWriter + + // mtu (maximum transmission unit) is the maximum size of a packet. mtu uint32 - rwc io.ReadWriteCloser - wg sync.WaitGroup + // caps holds the endpoint capabilities. + caps stack.LinkEndpointCapabilities - dispatcher stack.NetworkDispatcher - LinkEPCapabilities stack.LinkEndpointCapabilities + dispatcher stack.NetworkDispatcher } // New returns stack.LinkEndpoint(.*Endpoint) and error. -func New(rwc io.ReadWriteCloser, mtu uint32) (*Endpoint, error) { - switch { - case mtu == 0: +func New(rw io.ReadWriter, mtu uint32) (*Endpoint, error) { + if mtu == 0 { return nil, errors.New("MTU size is zero") - case rwc == nil: - return nil, errors.New("RWC interface is nil") + } + + if rw == nil { + return nil, errors.New("RW interface is nil") } return &Endpoint{ - rwc: rwc, + rw: rw, mtu: mtu, }, nil } @@ -51,12 +58,10 @@ func (e *Endpoint) IsAttached() bool { // dispatchLoop dispatches packets to upper layer. func (e *Endpoint) dispatchLoop() { - e.wg.Add(1) - defer e.wg.Done() - for { packet := make([]byte, e.mtu) - n, err := e.rwc.Read(packet) + + n, err := e.rw.Read(packet) if err != nil { break } @@ -79,8 +84,7 @@ func (e *Endpoint) dispatchLoop() { } } -// writePacket writes packets back into io.ReadWriter. -func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) *tcpip.Error { +func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error { networkHdr := pkt.NetworkHeader().View() transportHdr := pkt.TransportHeader().View() payload := pkt.Data.ToView() @@ -90,20 +94,20 @@ func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) *tcpip.Error { []buffer.View{networkHdr, transportHdr, payload}, ) - if _, err := e.rwc.Write(buf.ToView()); err != nil { - return tcpip.ErrInvalidEndpointState + if _, err := e.rw.Write(buf.ToView()); err != nil { + return &tcpip.ErrInvalidEndpointState{} } return nil } -// WritePacket writes packets back into io.ReadWriter. -func (e *Endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) *tcpip.Error { +// WritePacket writes packet back into io.ReadWriter. +func (e *Endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error { return e.writePacket(pkt) } // WritePackets writes packets back into io.ReadWriter. -func (e *Endpoint) WritePackets(_ *stack.Route, _ *stack.GSO, pkts stack.PacketBufferList, _ tcpip.NetworkProtocolNumber) (int, *tcpip.Error) { +func (e *Endpoint) WritePackets(_ stack.RouteInfo, _ *stack.GSO, pkts stack.PacketBufferList, _ tcpip.NetworkProtocolNumber) (int, tcpip.Error) { n := 0 for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { if err := e.writePacket(pkt); err != nil { @@ -115,7 +119,7 @@ func (e *Endpoint) WritePackets(_ *stack.Route, _ *stack.GSO, pkts stack.PacketB } // WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket. -func (e *Endpoint) WriteRawPacket(vv buffer.VectorisedView) *tcpip.Error { +func (e *Endpoint) WriteRawPacket(vv buffer.VectorisedView) tcpip.Error { pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ Data: vv, }) @@ -129,12 +133,12 @@ func (e *Endpoint) MTU() uint32 { // Capabilities implements stack.LinkEndpoint.Capabilities. func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities { - return e.LinkEPCapabilities + return e.caps } // GSOMaxSize returns the maximum GSO packet size. func (*Endpoint) GSOMaxSize() uint32 { - return 1 << 15 + return 1 << 15 /* default */ } // MaxHeaderLength returns the maximum size of the link layer header. Given it @@ -158,11 +162,4 @@ func (e *Endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.Network } // Wait implements stack.LinkEndpoint.Wait. -func (e *Endpoint) Wait() { - e.wg.Wait() -} - -// Close closes io.ReadWriteCloser. -func (e *Endpoint) Close() error { - return e.rwc.Close() -} +func (e *Endpoint) Wait() {} diff --git a/device/tun/opts.go b/device/tun/opts.go new file mode 100755 index 0000000..b1a474a --- /dev/null +++ b/device/tun/opts.go @@ -0,0 +1,15 @@ +package tun + +type Option func(*TUN) + +func WithName(name string) Option { + return func(t *TUN) { + t.name = name + } +} + +func WithMTU(mtu uint32) Option { + return func(t *TUN) { + t.mtu = mtu + } +} diff --git a/device/tun/opts_windows.go b/device/tun/opts_windows.go new file mode 100755 index 0000000..ea0bd29 --- /dev/null +++ b/device/tun/opts_windows.go @@ -0,0 +1,13 @@ +package tun + +func WithComponentID(componentID string) Option { + return func(t *TUN) { + t.componentID = componentID + } +} + +func WithNetwork(network string) Option { + return func(t *TUN) { + t.network = network + } +} diff --git a/device/tun/tun.go b/device/tun/tun.go new file mode 100755 index 0000000..fcafc67 --- /dev/null +++ b/device/tun/tun.go @@ -0,0 +1,2 @@ +// Package tun provides TUN which implemented device.Device interface. +package tun diff --git a/device/tun/tun_linux.go b/device/tun/tun_linux.go new file mode 100755 index 0000000..55d0802 --- /dev/null +++ b/device/tun/tun_linux.go @@ -0,0 +1,107 @@ +package tun + +import ( + "errors" + "fmt" + "unsafe" + + "github.com/xjasonlyu/tun2socks/device" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" + "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" + "gvisor.dev/gvisor/pkg/tcpip/link/tun" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +type TUN struct { + stack.LinkEndpoint + + fd int + mtu uint32 + name string +} + +func Open(opts ...Option) (device.Device, error) { + t := &TUN{} + + for _, opt := range opts { + opt(t) + } + + if len(t.name) >= unix.IFNAMSIZ { + return nil, errors.New("interface name too long") + } + + fd, err := tun.Open(t.name) + if err != nil { + return nil, fmt.Errorf("create tun: %w", err) + } + t.fd = fd + + if t.mtu > 0 { + if err := setMTU(t.name, t.mtu); err != nil { + return nil, fmt.Errorf("set mtu: %w", err) + } + } + + mtu, err := rawfile.GetMTU(t.name) + if err != nil { + return nil, fmt.Errorf("get mtu: %w", err) + } + t.mtu = mtu + + ep, err := fdbased.New(&fdbased.Options{ + MTU: t.mtu, + FDs: []int{fd}, + // TUN only + EthernetHeader: false, + }) + if err != nil { + return nil, fmt.Errorf("create endpoint: %w", err) + } + t.LinkEndpoint = ep + + return t, nil +} + +func (t *TUN) Name() string { + return t.name +} + +func (t *TUN) Close() error { + return unix.Close(t.fd) +} + +func setMTU(name string, n uint32) error { + // open datagram socket + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + return err + } + + defer unix.Close(fd) + + const ifReqSize = unix.IFNAMSIZ + 64 + + // do ioctl call + var ifr [ifReqSize]byte + copy(ifr[:], name) + *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = n + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return errors.New("failed to set MTU") + } + + return nil +} diff --git a/device/tun/tun_unix.go b/device/tun/tun_unix.go new file mode 100755 index 0000000..21c738e --- /dev/null +++ b/device/tun/tun_unix.go @@ -0,0 +1,80 @@ +// +build darwin freebsd openbsd + +package tun + +import ( + "fmt" + + "github.com/xjasonlyu/tun2socks/common/pool" + "github.com/xjasonlyu/tun2socks/device" + "github.com/xjasonlyu/tun2socks/device/rwbased" + + "golang.zx2c4.com/wireguard/tun" +) + +const offset = 4 /* 4 bytes TUN_PI */ + +type TUN struct { + *rwbased.Endpoint + + nt *tun.NativeTun + mtu uint32 + name string +} + +func Open(opts ...Option) (device.Device, error) { + t := &TUN{} + + for _, opt := range opts { + opt(t) + } + + nt, err := tun.CreateTUN(t.name, int(t.mtu)) + if err != nil { + return nil, fmt.Errorf("create tun: %w", err) + } + t.nt = nt.(*tun.NativeTun) + + mtu, err := nt.MTU() + if err != nil { + return nil, fmt.Errorf("get mtu: %w", err) + } + t.mtu = uint32(mtu) + + ep, err := rwbased.New(t, uint32(mtu)) + if err != nil { + return nil, fmt.Errorf("create endpoint: %w", err) + } + t.Endpoint = ep + + return t, nil +} + +func (t *TUN) Read(packet []byte) (n int, err error) { + buf := pool.Get(offset + len(packet)) + defer pool.Put(buf) + + if n, err = t.nt.Read(buf, offset); err != nil { + return + } + + copy(packet, buf[offset:offset+n]) + return +} + +func (t *TUN) Write(packet []byte) (int, error) { + buf := pool.Get(offset + len(packet)) + defer pool.Put(buf) + + copy(buf[offset:], packet) + return t.nt.Write(buf[:offset+len(packet)], offset) +} + +func (t *TUN) Name() string { + name, _ := t.nt.Name() + return name +} + +func (t *TUN) Close() error { + return t.nt.Close() +} diff --git a/device/tun/tun_windows.go b/device/tun/tun_windows.go new file mode 100755 index 0000000..c6b83d8 --- /dev/null +++ b/device/tun/tun_windows.go @@ -0,0 +1,66 @@ +package tun + +import ( + "fmt" + + "github.com/xjasonlyu/tun2socks/device" + "github.com/xjasonlyu/tun2socks/device/rwbased" + + "github.com/songgao/water" +) + +const defaultMTU = 1500 + +type TUN struct { + *rwbased.Endpoint + + iface *water.Interface + mtu uint32 + name string + + // windows only + componentID string + network string +} + +func Open(opts ...Option) (device.Device, error) { + t := &TUN{} + + for _, opt := range opts { + opt(t) + } + + iface, err := water.New(water.Config{ + DeviceType: water.TUN, + PlatformSpecificParams: water.PlatformSpecificParams{ + ComponentID: t.componentID, + InterfaceName: t.name, + Network: t.network, + }, + }) + if err != nil { + return nil, fmt.Errorf("create tun: %w", err) + } + t.iface = iface + + t.mtu = defaultMTU + if t.mtu > 0 { + /* ignore */ + } + + ep, err := rwbased.New(iface, t.mtu) + if err != nil { + return nil, fmt.Errorf("create endpoint: %w", err) + } + t.Endpoint = ep + + return t, nil +} + +func (t *TUN) Name() string { + return t.name +} + +func (t *TUN) Close() error { + return t.iface.Close() +} diff --git a/docker-compose.yml b/docker/docker-compose.yml similarity index 69% rename from docker-compose.yml rename to docker/docker-compose.yml index 5797820..1c5a7a9 100644 --- a/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: '2.4' services: tun2socks: - image: xjasonlyu/tun2socks:latest + image: ghcr.io/xjasonlyu/tun2socks:latest cap_add: - NET_ADMIN devices: @@ -11,15 +11,17 @@ services: - GODEBUG=madvdontneed=1 - PROXY= - LOGLEVEL= - - API= - - DNS= - - HOSTS= + - STATS= + - SECRET= - EXCLUDED= - EXTRACMD= networks: switch: ipv4_address: 172.20.1.2 - restart: always + sysctls: + - net.ipv4.ip_forward=1 + - net.ipv4.conf.all.rp_filter=0 + restart: unless-stopped container_name: tun2socks networks: @@ -28,7 +30,7 @@ networks: ipam: driver: default config: - - subnet: '172.20.1.0/25' + - subnet: '172.20.1.0/24' gateway: 172.20.1.1 driver: macvlan driver_opts: diff --git a/scripts/entrypoint.sh b/docker/entrypoint.sh similarity index 89% rename from scripts/entrypoint.sh rename to docker/entrypoint.sh index 3756edb..5b25d36 100644 --- a/scripts/entrypoint.sh +++ b/docker/entrypoint.sh @@ -59,18 +59,14 @@ main() { sh -c "$EXTRACMD" fi - if [ -n "$API" ]; then - ARGS="--api $API" + if [ -n "$STATS" ]; then + ARGS="--stats $STATS" fi - if [ -n "$DNS" ]; then - ARGS="$ARGS --dns $DNS" + if [ -n "$SECRET" ]; then + ARGS="$ARGS --secret $SECRET" fi - for item in $(echo "$HOSTS" | tr ',' '\n'); do - ARGS="$ARGS --hosts $item" - done - exec tun2socks \ --loglevel "$LOGLEVEL" \ --interface "$ETH" \ diff --git a/scripts/load_tun.sh b/docker/load_tun.sh similarity index 100% rename from scripts/load_tun.sh rename to docker/load_tun.sh diff --git a/engine/engine.go b/engine/engine.go new file mode 100755 index 0000000..25621ad --- /dev/null +++ b/engine/engine.go @@ -0,0 +1,111 @@ +package engine + +import ( + "github.com/xjasonlyu/tun2socks/component/dialer" + "github.com/xjasonlyu/tun2socks/device" + "github.com/xjasonlyu/tun2socks/log" + "github.com/xjasonlyu/tun2socks/proxy" + "github.com/xjasonlyu/tun2socks/stack" + "github.com/xjasonlyu/tun2socks/stats" +) + +type Engine struct { + mtu uint32 + iface string + secret string + stats string + logLevel string + rawProxy string + rawDevice string + + device device.Device +} + +func New(opts ...Option) *Engine { + e := &Engine{} + + for _, opt := range opts { + opt(e) + } + return e +} + +func (e *Engine) Start() error { + for _, set := range []func() error{ + e.setLogLevel, + e.setInterface, + e.setStats, + e.setProxy, + e.setDevice, + e.setStack, + } { + if err := set(); err != nil { + return err + } + } + return nil +} + +func (e *Engine) Stop() { + if e.device != nil { + if err := e.device.Close(); err != nil { + log.Fatalf("%v", err) + } + } +} + +func (e *Engine) setLogLevel() error { + level, err := log.ParseLevel(e.logLevel) + if err != nil { + return err + } + log.SetLevel(level) + return nil +} + +func (e *Engine) setInterface() error { + if e.iface != "" { + if err := dialer.BindToInterface(e.iface); err != nil { + return err + } + log.Infof("[BOUND] bind to interface: %s", e.iface) + } + return nil +} + +func (e *Engine) setStats() error { + if e.stats != "" { + go func() { + _ = stats.Start(e.stats, e.secret) + }() + log.Infof("[STATS] listen and serve at: http://%s", e.stats) + } + return nil +} + +func (e *Engine) setProxy() error { + d, err := parseProxy(e.rawProxy) + if err != nil { + return err + } + proxy.SetDialer(d) + return nil +} + +func (e *Engine) setDevice() error { + d, err := parseDevice(e.rawDevice, e.mtu) + if err != nil { + return err + } + e.device = d + return nil +} + +func (e *Engine) setStack() error { + handler := &fakeTunnel{} + if _, err := stack.New(e.device, handler, stack.WithDefault()); err != nil { + return err + } + log.Infof("[STACK] %s <-> %s", e.rawDevice, e.rawProxy) + return nil +} diff --git a/engine/opts.go b/engine/opts.go new file mode 100755 index 0000000..8dc85e9 --- /dev/null +++ b/engine/opts.go @@ -0,0 +1,40 @@ +package engine + +type Option func(*Engine) + +func WithDevice(device string) Option { + return func(e *Engine) { + e.rawDevice = device + } +} + +func WithInterface(iface string) Option { + return func(e *Engine) { + e.iface = iface + } +} + +func WithLogLevel(level string) Option { + return func(e *Engine) { + e.logLevel = level + } +} + +func WithMTU(mtu int) Option { + return func(e *Engine) { + e.mtu = uint32(mtu) + } +} + +func WithProxy(proxy string) Option { + return func(e *Engine) { + e.rawProxy = proxy + } +} + +func WithStats(stats, secret string) Option { + return func(e *Engine) { + e.stats = stats + e.secret = secret + } +} diff --git a/engine/parse.go b/engine/parse.go new file mode 100644 index 0000000..b65bc19 --- /dev/null +++ b/engine/parse.go @@ -0,0 +1,69 @@ +package engine + +import ( + "fmt" + "net/url" + "strings" + + "github.com/xjasonlyu/tun2socks/device" + "github.com/xjasonlyu/tun2socks/device/tun" + "github.com/xjasonlyu/tun2socks/proxy" +) + +func parseDevice(s string, mtu uint32) (device.Device, error) { + const defaultDriver = "tun" + if !strings.Contains(s, "://") { + s = defaultDriver + "://" + s + } + + u, err := url.Parse(s) + if err != nil { + return nil, err + } + + name := u.Host + driver := u.Scheme + + var d device.Device + + switch driver { + case "tun": + d, err = tun.Open(tun.WithName(name), tun.WithMTU(mtu)) + default: + err = fmt.Errorf("unsupported driver: %s", driver) + } + if err != nil { + return nil, err + } + + return d, nil +} + +func parseProxy(s string) (proxy.Dialer, error) { + const defaultProto = "socks5" + if !strings.Contains(s, "://") { + s = defaultProto + "://" + s + } + + u, err := url.Parse(s) + if err != nil { + return nil, err + } + + addr := u.Host + user := u.User.Username() + pass, _ := u.User.Password() + proto := strings.ToLower(u.Scheme) + + switch proto { + case "direct": + return proxy.NewDirect(), nil + case "socks5": + return proxy.NewSocks5(addr, user, pass) + case "ss", "shadowsocks": + method, password := user, pass + return proxy.NewShadowSocks(addr, method, password) + } + + return nil, fmt.Errorf("unsupported protocol: %s", proto) +} diff --git a/engine/tunnel.go b/engine/tunnel.go new file mode 100644 index 0000000..3fb771d --- /dev/null +++ b/engine/tunnel.go @@ -0,0 +1,19 @@ +package engine + +import ( + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/stack" + "github.com/xjasonlyu/tun2socks/tunnel" +) + +var _ stack.Handler = (*fakeTunnel)(nil) + +type fakeTunnel struct{} + +func (*fakeTunnel) Add(conn adapter.TCPConn) { + tunnel.Add(conn) +} + +func (*fakeTunnel) AddPacket(packet adapter.UDPPacket) { + tunnel.AddPacket(packet) +} diff --git a/go.mod b/go.mod index aa89d8b..f0279fd 100644 --- a/go.mod +++ b/go.mod @@ -7,18 +7,17 @@ require ( github.com/go-chi/chi v4.1.2+incompatible github.com/go-chi/cors v1.1.1 github.com/go-chi/render v1.0.1 - github.com/gofrs/uuid v3.3.0+incompatible + github.com/gofrs/uuid v4.0.0+incompatible github.com/gorilla/websocket v1.4.2 - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.7.0 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/urfave/cli/v2 v2.3.0 - github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6 + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.5.1 go.uber.org/atomic v1.7.0 - golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect - golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e - golang.zx2c4.com/wireguard v0.0.20200320 - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect - gvisor.dev/gvisor v0.0.0-20201107072535-9e848922ed33 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c + golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 + golang.zx2c4.com/wireguard v0.0.20201118 + gvisor.dev/gvisor v0.0.0-20210205020539-1dbf4e4527a5 ) diff --git a/go.sum b/go.sum index 0375605..467af42 100644 --- a/go.sum +++ b/go.sum @@ -6,11 +6,33 @@ cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6A cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.52.1-0.20200122224058-0482b626c726/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= @@ -23,8 +45,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Dreamacro/go-shadowsocks2 v0.1.6 h1:PysSf9sLT3Qn8jhlin5v7Rk68gOQG4K5BZFY1nxLGxI= github.com/Dreamacro/go-shadowsocks2 v0.1.6/go.mod h1:LSXCjyHesPY3pLjhwff1mQX72ItcBT/N2xNC685cYeU= -github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -34,18 +57,30 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/cgroups v0.0.0-20181219155423-39b18af02c41/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= @@ -61,13 +96,14 @@ github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/dpjacques/clockwork v0.1.1-0.20200827220843-c1f524b839be/go.mod h1:D8mP2A8vVT2GkXqPorSBmhnshhkFBYgzhA90KmJt25Y= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -78,17 +114,18 @@ github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw= github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -96,20 +133,29 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -117,20 +163,33 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3-0.20201020212313-ab46b8bd0abd/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github/v28 v28.1.2-0.20191108005307-e555eab49ce8/go.mod h1:g82e6OHbJ0WYrYeOrid1MMfHAtqjxBz+N74tfAt9KrQ= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210115211752-39141e76b647/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -139,11 +198,12 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -163,8 +223,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= -github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -178,17 +236,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20181111125026-1722abf79c2f/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/oschwald/geoip2-golang v1.4.0 h1:5RlrjCgRyIGDz/mBmPfnAF4h8k0IAcRv9PvrpOfz+Ug= -github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng= -github.com/oschwald/maxminddb-golang v1.6.0 h1:KAJSjdHQ8Kv45nFIbtoLGrGWqHFajOIm7skTyz/+Dls= -github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w= -github.com/oschwald/maxminddb-golang v1.7.0 h1:JmU4Q1WBv5Q+2KZy5xJI+98aUwTIrPPxZUkd5Cwr8Zc= -github.com/oschwald/maxminddb-golang v1.7.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -197,12 +251,11 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -216,6 +269,7 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= @@ -224,21 +278,26 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6 h1:iQsLkjayjJs29VOeXaeznpy1jddiuRjstj6MfFbKVoM= -github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6/go.mod h1:eQ5eRTAjXlzRV4rB9pOujCwDl1mBF8htrMvKMqavqvo= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= @@ -247,18 +306,24 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -267,13 +332,20 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -286,26 +358,46 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= @@ -323,27 +415,46 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -357,8 +468,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -376,27 +487,67 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201021000207-d49c4edd7d96/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g= -golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY= +golang.zx2c4.com/wireguard v0.0.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU= +golang.zx2c4.com/wireguard v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -405,21 +556,59 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.36.0-dev.0.20210122012134-2c42474aca0c/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= @@ -430,22 +619,19 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gvisor.dev/gvisor v0.0.0-20201107072535-9e848922ed33 h1:Iy80PPGcHGhOacbFJk/Cn8lV0k8dcg8UJBo8e5qV9Ac= -gvisor.dev/gvisor v0.0.0-20201107072535-9e848922ed33/go.mod h1:t4GUXJhnQEPtYSvrvRNW5CNdBQ2oWZBy7P+FbCVKBFY= +gvisor.dev/gvisor v0.0.0-20210205020539-1dbf4e4527a5 h1:rXHdBmJgbj9e4FQHMUfvH+/QDIeh+GJT11TcOslPP30= +gvisor.dev/gvisor v0.0.0-20210205020539-1dbf4e4527a5/go.mod h1:bGHhdxWoqL+3AwdMfePr6TWTcvHO33egRmxDPAQFdJE= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs= k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= @@ -457,5 +643,7 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/internal/api/errors.go b/internal/api/errors.go deleted file mode 100644 index 7a24f3a..0000000 --- a/internal/api/errors.go +++ /dev/null @@ -1,24 +0,0 @@ -// Clash: https://github.com/Dreamacro/clash/blob/master/hub/route/errors.go - -package api - -var ( - ErrUnauthorized = newError("Unauthorized") - ErrBadRequest = newError("Body invalid") - ErrForbidden = newError("Forbidden") - ErrNotFound = newError("Resource not found") - ErrRequestTimeout = newError("Timeout") -) - -// HTTPError is custom HTTP error for API -type HTTPError struct { - Message string `json:"message"` -} - -func (e *HTTPError) Error() string { - return e.Message -} - -func newError(msg string) *HTTPError { - return &HTTPError{Message: msg} -} diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go deleted file mode 100644 index e31dc44..0000000 --- a/internal/cmd/cmd.go +++ /dev/null @@ -1,95 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "os/signal" - "runtime" - "syscall" - "time" - - "github.com/urfave/cli/v2" - - "github.com/xjasonlyu/clash/component/dialer" - "github.com/xjasonlyu/tun2socks/internal/api" - "github.com/xjasonlyu/tun2socks/internal/core" - "github.com/xjasonlyu/tun2socks/internal/dns" - "github.com/xjasonlyu/tun2socks/internal/proxy" - "github.com/xjasonlyu/tun2socks/internal/tunnel" - "github.com/xjasonlyu/tun2socks/pkg/log" - "github.com/xjasonlyu/tun2socks/pkg/tun" -) - -func bindToInterface(name string) { - dialer.DialHook = dialer.DialerWithInterface(name) - dialer.ListenPacketHook = dialer.ListenPacketWithInterface(name) -} - -func printVersion(app *cli.App) { - fmt.Printf("%s %s\n%s/%s, %s, %s\n", - app.Name, - app.Version, - runtime.GOOS, - runtime.GOARCH, - runtime.Version(), - app.Compiled.Format(time.RFC3339), - ) -} - -func Main(c *cli.Context) error { - if c.Bool("version") { - printVersion(c.App) - return nil - } - - level, err := log.ParseLevel(c.String("loglevel")) - if err != nil { - return err - } - log.SetLevel(level) - - if c.IsSet("interface") { - name := c.String("interface") - bindToInterface(name) - log.Infof("[DIALER] bind to interface: %s", name) - } - - if c.IsSet("api") { /* initiate API */ - raw := c.String("api") - if err := api.Start(raw, c.App); err != nil { - return fmt.Errorf("start API server %s: %w", raw, err) - } - log.Infof("[API] listen and serve at: %s", raw) - } - - if c.IsSet("dns") { /* initiate DNS */ - raw := c.String("dns") - if err := dns.Start(raw, c.StringSlice("hosts")); err != nil { - return fmt.Errorf("start DNS server %s: %w", raw, err) - } - log.Infof("[DNS] listen and serve at: %s", raw) - } - - deviceURL := c.String("device") - device, err := tun.Open(deviceURL) - if err != nil { - return fmt.Errorf("open device %s: %w", deviceURL, err) - } - defer device.Close() - - proxyURL := c.String("proxy") - if err := proxy.Register(proxyURL); err != nil { - return fmt.Errorf("register proxy %s: %w", proxyURL, err) - } - - if _, err := core.NewDefaultStack(device, tunnel.Add, tunnel.AddPacket); err != nil { - return fmt.Errorf("initiate stack: %w", err) - } - log.Infof("[STACK] %s --> %s", deviceURL, proxyURL) - - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) - <-sigCh - - return nil -} diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go deleted file mode 100644 index 9fc3655..0000000 --- a/internal/cmd/flags.go +++ /dev/null @@ -1,51 +0,0 @@ -package cmd - -import "github.com/urfave/cli/v2" - -var ( - API = cli.StringFlag{ - Name: "api", - Usage: "URL of external API to listen", - } - - Device = cli.StringFlag{ - Name: "device", - Aliases: []string{"d"}, - Usage: "URL of device to open", - } - - DNS = cli.StringFlag{ - Name: "dns", - Usage: "URL of fake DNS to listen", - } - - Hosts = cli.StringSliceFlag{ - Name: "hosts", - Usage: "Extra hosts mapping", - } - - Interface = cli.StringFlag{ - Name: "interface", - Aliases: []string{"i"}, - Usage: "Bind interface to dial", - } - - LogLevel = cli.StringFlag{ - Name: "loglevel", - Aliases: []string{"l"}, - Usage: "Set logging level", - Value: "INFO", - } - - Proxy = cli.StringFlag{ - Name: "proxy", - Aliases: []string{"p"}, - Usage: "URL of proxy to dial", - } - - Version = cli.BoolFlag{ - Name: "version", - Aliases: []string{"v"}, - Usage: "Print current version", - } -) diff --git a/internal/core/stack.go b/internal/core/stack.go deleted file mode 100644 index e60faf0..0000000 --- a/internal/core/stack.go +++ /dev/null @@ -1,93 +0,0 @@ -package core - -import ( - "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" - "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" - "gvisor.dev/gvisor/pkg/tcpip/stack" - "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" - "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" -) - -// NewStack returns *stack.Stack with provided options. -func NewStack(opts ...Option) (*stack.Stack, error) { - ipstack := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ - ipv4.NewProtocol, - ipv6.NewProtocol, - }, - TransportProtocols: []stack.TransportProtocolFactory{ - tcp.NewProtocol, - udp.NewProtocol, - icmp.NewProtocol4, - icmp.NewProtocol6, - }, - }) - - for _, opt := range opts { - if err := opt(ipstack); err != nil { - return nil, err - } - } - - return ipstack, nil -} - -// NewDefaultStack calls NewStack with default options. -func NewDefaultStack(linkEp stack.LinkEndpoint, th tcpHandleFunc, uh udpHandleFunc) (*stack.Stack, error) { - return NewStack( - WithDefaultTTL(defaultTimeToLive), - WithForwarding(ipForwardingEnabled), - - // Config default stack ICMP settings. - WithICMPBurst(icmpBurst), WithICMPLimit(icmpLimit), - - // We expect no packet loss, therefore we can bump buffers. - // Too large buffers thrash cache, so there is little point - // in too large buffers. - // - // Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go - WithTCPBufferSizeRange(minBufferSize, defaultBufferSize, maxBufferSize), - - WithTCPCongestionControl(tcpCongestionControlAlgorithm), - WithTCPDelay(tcpDelayEnabled), - - // Receive Buffer Auto-Tuning Option, see: - // https://github.com/google/gvisor/issues/1666 - WithTCPModerateReceiveBuffer(tcpModerateReceiveBufferEnabled), - - // TCP selective ACK Option, see: - // https://tools.ietf.org/html/rfc2018 - WithTCPSACKEnabled(tcpSACKEnabled), - - // Important: We must initiate transport protocol handlers - // before creating NIC, otherwise NIC would dispatch packets - // to stack and cause race condition. - WithICMPHandler(nil), WithTCPHandler(th), WithUDPHandler(uh), - - // Create stack NIC and then bind link endpoint. - WithCreatingNIC(defaultNICID, linkEp), - - // In past we did s.AddAddressRange to assign 0.0.0.0/0 onto - // the interface. We need that to be able to terminate all the - // incoming connections - to any ip. AddressRange API has been - // removed and the suggested workaround is to use Promiscuous - // mode. https://github.com/google/gvisor/issues/3876 - // - // Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go - WithPromiscuousMode(defaultNICID, nicPromiscuousModeEnabled), - - // Enable spoofing if a stack may send packets from unowned addresses. - // This change required changes to some netgophers since previously, - // promiscuous mode was enough to let the netstack respond to all - // incoming packets regardless of the packet's destination address. Now - // that a stack.Route is not held for each incoming packet, finding a route - // may fail with local addresses we don't own but accepted packets for - // while in promiscuous mode. Since we also want to be able to send from - // any address (in response the received promiscuous mode packets), we need - // to enable spoofing. - // - // Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127 - WithSpoofing(defaultNICID, nicSpoofingEnabled), - ) -} diff --git a/internal/core/udp.go b/internal/core/udp.go deleted file mode 100644 index b132c9b..0000000 --- a/internal/core/udp.go +++ /dev/null @@ -1,146 +0,0 @@ -package core - -import ( - "fmt" - "net" - _ "unsafe" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/stack" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" - - "github.com/xjasonlyu/tun2socks/internal/adapter" - "github.com/xjasonlyu/tun2socks/pkg/log" -) - -const udpNoChecksum = true - -type udpHandleFunc func(adapter.UDPPacket) - -func WithUDPHandler(handle udpHandleFunc) Option { - return func(s *stack.Stack) error { - udpHandlePacket := func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool { - // Ref: gVisor pkg/tcpip/transport/udp/endpoint.go HandlePacket - udpHdr := header.UDP(pkt.TransportHeader().View()) - if int(udpHdr.Length()) > pkt.Data.Size()+header.UDPMinimumSize { - // Malformed packet. - s.Stats().UDP.MalformedPacketsReceived.Increment() - return true - } - - if !verifyChecksum(udpHdr, pkt) { - // Checksum error. - s.Stats().UDP.ChecksumErrors.Increment() - return true - } - - s.Stats().UDP.PacketsReceived.Increment() - - netHdr := pkt.Network() - route, err := s.FindRoute(pkt.NICID, netHdr.DestinationAddress(), netHdr.SourceAddress(), pkt.NetworkProtocolNumber, false /* multicastLoop */) - if err != nil { - log.Warnf("[STACK] find route error: %v", err) - return true - } - route.ResolveWith(pkt.SourceLinkAddress()) - - packet := &udpPacket{ - id: id, - r: &route, - metadata: &adapter.Metadata{ - Net: adapter.UDP, - SrcIP: net.IP(id.RemoteAddress), - SrcPort: id.RemotePort, - DstIP: net.IP(id.LocalAddress), - DstPort: id.LocalPort, - }, - payload: pkt.Data.ToView(), - } - - handle(packet) - return true - } - s.SetTransportProtocolHandler(udp.ProtocolNumber, udpHandlePacket) - return nil - } -} - -type udpPacket struct { - id stack.TransportEndpointID - r *stack.Route - metadata *adapter.Metadata - payload []byte -} - -func (p *udpPacket) Data() []byte { - return p.payload -} - -func (p *udpPacket) Drop() { - p.r.Release() -} - -func (p *udpPacket) LocalAddr() net.Addr { - return &net.UDPAddr{IP: net.IP(p.id.LocalAddress), Port: int(p.id.LocalPort)} -} - -func (p *udpPacket) Metadata() *adapter.Metadata { - return p.metadata -} - -func (p *udpPacket) RemoteAddr() net.Addr { - return &net.UDPAddr{IP: net.IP(p.id.RemoteAddress), Port: int(p.id.RemotePort)} -} - -func (p *udpPacket) WriteBack(b []byte, addr net.Addr) (int, error) { - v := buffer.View(b) - if len(v) > header.UDPMaximumPacketSize { - // Payload can't possibly fit in a packet. - return 0, fmt.Errorf("%s", tcpip.ErrMessageTooLong) - } - - data := v.ToVectorisedView() - // if addr is not provided, write back use original dst Addr as src Addr. - if addr == nil { - return _sendUDP(p.r, data, p.id.LocalPort, p.id.RemotePort, udpNoChecksum) - } - - udpAddr, ok := addr.(*net.UDPAddr) - if !ok { - return 0, fmt.Errorf("type %T is not a valid udp address", addr) - } - - r := p.r.Clone() - defer r.Release() - - if ipv4 := udpAddr.IP.To4(); ipv4 != nil { - r.LocalAddress = tcpip.Address(ipv4) - } else { - r.LocalAddress = tcpip.Address(udpAddr.IP) - } - return _sendUDP(&r, data, uint16(udpAddr.Port), p.id.RemotePort, udpNoChecksum) -} - -// _sendUDP wraps sendUDP with some default parameters. -func _sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, noChecksum bool) (int, error) { - if err := sendUDP(r, data, localPort, remotePort, 0 /* ttl */, true /* useDefaultTTL */, 0 /* tos */, nil /* owner */, noChecksum); err != nil { - return 0, fmt.Errorf("%s", err) - } - return data.Size(), nil -} - -// sendUDP sends a UDP segment via the provided network endpoint and under the -// provided identity. -// -//go:linkname sendUDP gvisor.dev/gvisor/pkg/tcpip/transport/udp.sendUDP -func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8, owner tcpip.PacketOwner, noChecksum bool) *tcpip.Error - -// verifyChecksum verifies the checksum unless RX checksum offload is enabled. -// On IPv4, UDP checksum is optional, and a zero value means the transmitter -// omitted the checksum generation (RFC768). -// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1). -// -//go:linkname verifyChecksum gvisor.dev/gvisor/pkg/tcpip/transport/udp.verifyChecksum -func verifyChecksum(hdr header.UDP, pkt *stack.PacketBuffer) bool diff --git a/internal/dns/resolver.go b/internal/dns/resolver.go deleted file mode 100644 index 5fbf46f..0000000 --- a/internal/dns/resolver.go +++ /dev/null @@ -1,18 +0,0 @@ -package dns - -import ( - "net" - - "github.com/xjasonlyu/clash/component/dialer" - "github.com/xjasonlyu/clash/component/resolver" -) - -func init() { - - // enable ipv6 - resolver.DisableIPv6 = false - - // use bound dialer to resolve DNS - net.DefaultResolver.PreferGo = true - net.DefaultResolver.Dial = dialer.DialContext -} diff --git a/internal/dns/server.go b/internal/dns/server.go deleted file mode 100644 index d7bea85..0000000 --- a/internal/dns/server.go +++ /dev/null @@ -1,110 +0,0 @@ -package dns - -import ( - "errors" - "net/url" - "strings" - - "github.com/xjasonlyu/clash/component/resolver" - "github.com/xjasonlyu/clash/dns" -) - -const ( - defaultScheme = "dns" - defaultCacheSize = 1000 - defaultFakeIPRange = "198.18.0.0/15" -) - -var _defaultNameServer = []string{"223.5.5.5", "8.8.8.8"} - -func Start(dnsURL string, rawHosts []string) error { - if !strings.Contains(dnsURL, "://") { - dnsURL = defaultScheme + "://" + dnsURL - } - - u, err := url.Parse(dnsURL) - if err != nil { - return err - } - - if strings.ToLower(u.Scheme) != defaultScheme { - return errors.New("unsupported scheme") - } - - serverAddr := u.Host - if serverAddr == "" { - return nil - } - - fakeIPRange := u.Query().Get("fake-ip-range") - if fakeIPRange == "" { - fakeIPRange = defaultFakeIPRange - } - - var fakeIPFilter []string - if raw := strings.TrimSpace(u.Query().Get("fake-ip-filter")); raw != "" { - fakeIPFilter = append(fakeIPFilter, strings.Split(raw, ",")...) - } - - pool, err := parseFakeIP(fakeIPRange, fakeIPFilter) - if err != nil { - return err - } - - var mainNS, defaultNS []dns.NameServer - { - var ns []string - if raw := strings.TrimSpace(u.Query().Get("default-nameserver")); raw != "" { - ns = append(ns, strings.Split(raw, ",")...) - } - - if len(ns) == 0 { - ns = append(ns, _defaultNameServer...) - } - - if defaultNS, err = parseNameServer(ns); err != nil { - return err - } - } - { - var ns []string - if raw := strings.TrimSpace(u.Query().Get("nameserver")); raw != "" { - ns = append(ns, strings.Split(raw, ",")...) - } - - if len(ns) == 0 { - ns = append(ns, _defaultNameServer...) - } - - if mainNS, err = parseNameServer(ns); err != nil { - return err - } - } - - hosts, err := parseHosts(parseHostsSlice(rawHosts)) - if err != nil { - return err - } - - cfg := dns.Config{ - IPv6: true, - Pool: pool, - Hosts: hosts, - Main: mainNS, - Default: defaultNS, - EnhancedMode: dns.FAKEIP, - } - - r := dns.NewResolver(cfg) - m := dns.NewEnhancer(cfg) - - // reuse cache of old host mapper - if old := resolver.DefaultHostMapper; old != nil { - m.PatchFrom(old.(*dns.ResolverEnhancer)) - } - - resolver.DefaultResolver = r - resolver.DefaultHostMapper = m - - return dns.ReCreateServer(serverAddr, r, m) -} diff --git a/internal/dns/utils.go b/internal/dns/utils.go deleted file mode 100644 index 284e703..0000000 --- a/internal/dns/utils.go +++ /dev/null @@ -1,141 +0,0 @@ -package dns - -import ( - "fmt" - "net" - "net/url" - "strings" - - "github.com/xjasonlyu/clash/component/fakeip" - "github.com/xjasonlyu/clash/component/trie" - "github.com/xjasonlyu/clash/dns" - "github.com/xjasonlyu/tun2socks/pkg/log" -) - -func parseFakeIP(fakeIPRange string, fakeIPFilter []string) (*fakeip.Pool, error) { - _, ipnet, err := net.ParseCIDR(fakeIPRange) - if err != nil { - return nil, err - } - - var host *trie.DomainTrie - // fake ip skip host filter - if len(fakeIPFilter) != 0 { - host = trie.New() - for _, domain := range fakeIPFilter { - _ = host.Insert(domain, true) - } - } - - return fakeip.New(ipnet, defaultCacheSize, host) -} - -func parseHosts(hosts map[string]string) (*trie.DomainTrie, error) { - tree := trie.New() - - // add default hosts - if err := tree.Insert("localhost", net.IP{127, 0, 0, 1}); err != nil { - log.Errorf("[DNS] insert localhost to host error: %v", err) - } - - if len(hosts) != 0 { - for domain, ipStr := range hosts { - ip := net.ParseIP(ipStr) - if ip == nil { - return nil, fmt.Errorf("%s is not a valid IP", ipStr) - } - _ = tree.Insert(domain, ip) - } - } - - return tree, nil -} - -func parseHostsSlice(s []string) map[string]string { - m := make(map[string]string) - - for _, i := range s { - if strings.Contains(i, "=") { - v := strings.SplitN(i, "=", 2) - m[v[0]] = v[1] - continue - } - if strings.Contains(i, ":") { - v := strings.SplitN(i, ":", 2) - m[v[0]] = v[1] - continue - } - log.Debugf("invalid hosts item: %s", i) - } - - return m -} - -func hostWithDefaultPort(host string, defPort string) (string, error) { - if !strings.Contains(host, ":") { - host += ":" - } - - hostname, port, err := net.SplitHostPort(host) - if err != nil { - return "", err - } - - if port == "" { - port = defPort - } - - return net.JoinHostPort(hostname, port), nil -} - -func parseNameServer(servers []string) ([]dns.NameServer, error) { - var nameservers []dns.NameServer - - for idx, server := range servers { - server := strings.TrimSpace(server) - if server == "" { - return nil, fmt.Errorf("empty server field") - } - - // parse without scheme .e.g 8.8.8.8:53 - if !strings.Contains(server, "://") { - server = "udp://" + server - } - u, err := url.Parse(server) - if err != nil { - return nil, fmt.Errorf("DNS NameServer[%d] format: %s", idx, err.Error()) - } - - var addr, dnsNetType string - switch u.Scheme { - case "udp": - addr, err = hostWithDefaultPort(u.Host, "53") - dnsNetType = "" // UDP - case "tcp": - addr, err = hostWithDefaultPort(u.Host, "53") - dnsNetType = "tcp" // TCP - case "tls": - addr, err = hostWithDefaultPort(u.Host, "853") - dnsNetType = "tcp-tls" // DNS over TLS - case "https": - clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path} - addr = clearURL.String() - dnsNetType = "https" // DNS over HTTPS - default: - return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme) - } - - if err != nil { - return nil, fmt.Errorf("DNS NameServer[%d] format: %s", idx, err.Error()) - } - - nameservers = append( - nameservers, - dns.NameServer{ - Net: dnsNetType, - Addr: addr, - }, - ) - } - return nameservers, nil -} diff --git a/internal/proxy/base.go b/internal/proxy/base.go deleted file mode 100755 index f2af7a1..0000000 --- a/internal/proxy/base.go +++ /dev/null @@ -1,50 +0,0 @@ -package proxy - -import ( - "context" - "errors" - "net" - "net/url" - "strings" - - "github.com/xjasonlyu/tun2socks/internal/adapter" -) - -type Base struct { - url *url.URL -} - -func NewBase(url *url.URL) (*Base, error) { - return &Base{ - url: url, - }, nil -} - -func (b *Base) Type() string { - if b.url == nil { - return "" - } - return strings.ToLower(b.url.Scheme) -} - -func (b *Base) Addr() string { - if b.url == nil { - return "" - } - return b.url.Host -} - -func (b *Base) String() string { - if b.url == nil { - return "" - } - return b.url.String() -} - -func (b *Base) DialContext(context.Context, *adapter.Metadata) (net.Conn, error) { - return nil, errors.New("no support") -} - -func (b *Base) DialUDP(*adapter.Metadata) (net.PacketConn, error) { - return nil, errors.New("no support") -} diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go deleted file mode 100755 index 5748deb..0000000 --- a/internal/proxy/proxy.go +++ /dev/null @@ -1,84 +0,0 @@ -package proxy - -import ( - "context" - "fmt" - "net" - "net/url" - "strings" - - "github.com/xjasonlyu/tun2socks/internal/adapter" -) - -type Dialer interface { - Addr() string - Type() string - String() string - - DialContext(context.Context, *adapter.Metadata) (net.Conn, error) - DialUDP(*adapter.Metadata) (net.PacketConn, error) -} - -var _defaultDialer Dialer = &Base{} - -// New returns proxy dialer. -func New(proxyURL string) (Dialer, error) { - u, err := url.Parse(proxyURL) - if err != nil { - return nil, err - } - - proto := strings.ToLower(u.Scheme) - user := u.User.Username() - pass, _ := u.User.Password() - - switch proto { - case "direct": - return NewDirect(u) - case "socks5": - return NewSocks5(u, user, pass) - case "ss", "shadowsocks": - method, password := user, pass - return NewShadowSocks(u, method, password) - } - - return nil, fmt.Errorf("unsupported protocol: %s", proto) -} - -// Register updates the _defaultDialer. -func Register(proxyURL string) error { - dialer, err := New(proxyURL) - if err != nil { - return err - } - - _defaultDialer = dialer - return nil -} - -// Dial uses _defaultDialer to dial TCP. -func Dial(metadata *adapter.Metadata) (net.Conn, error) { - ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout) - defer cancel() - return _defaultDialer.DialContext(ctx, metadata) -} - -// DialUDP uses _defaultDialer to dial UDP. -func DialUDP(metadata *adapter.Metadata) (net.PacketConn, error) { - return _defaultDialer.DialUDP(metadata) -} - -// Addr returns _defaultDialer addr. -func Addr() string { - return _defaultDialer.Addr() -} - -// Type returns _defaultDialer type. -func Type() string { - return _defaultDialer.Type() -} - -// String returns _defaultDialer URL. -func String() string { - return _defaultDialer.String() -} diff --git a/internal/proxy/utils.go b/internal/proxy/utils.go deleted file mode 100755 index 5173ba2..0000000 --- a/internal/proxy/utils.go +++ /dev/null @@ -1,33 +0,0 @@ -package proxy - -import ( - "net" - "time" - - "github.com/xjasonlyu/clash/component/resolver" -) - -const ( - tcpConnectTimeout = 5 * time.Second - tcpKeepAlivePeriod = 30 * time.Second -) - -func tcpKeepAlive(c net.Conn) { - if tcp, ok := c.(*net.TCPConn); ok { - tcp.SetKeepAlive(true) - tcp.SetKeepAlivePeriod(tcpKeepAlivePeriod) - } -} - -func resolveUDPAddr(network, address string) (*net.UDPAddr, error) { - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, err - } - - ip, err := resolver.ResolveIP(host) - if err != nil { - return nil, err - } - return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port)) -} diff --git a/internal/tunnel/utils.go b/internal/tunnel/utils.go deleted file mode 100644 index dffbb58..0000000 --- a/internal/tunnel/utils.go +++ /dev/null @@ -1,35 +0,0 @@ -package tunnel - -import ( - "fmt" - - "github.com/xjasonlyu/clash/component/resolver" - "github.com/xjasonlyu/tun2socks/internal/adapter" -) - -func generateNATKey(m *adapter.Metadata) string { - return m.SourceAddress() /* Full Cone NAT Key */ -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -func resolveMetadata(metadata *adapter.Metadata) error { - if metadata.DstIP == nil { - return fmt.Errorf("destination IP is nil") - } - - if resolver.IsFakeIP(metadata.DstIP) { - var exist bool - metadata.Host, exist = resolver.FindHostByIP(metadata.DstIP) - if !exist { - return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) - } - } - - return nil -} diff --git a/pkg/log/event.go b/log/event.go old mode 100644 new mode 100755 similarity index 92% rename from pkg/log/event.go rename to log/event.go index c250408..6d05c07 --- a/pkg/log/event.go +++ b/log/event.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/xjasonlyu/clash/common/observable" + "github.com/xjasonlyu/tun2socks/common/observable" ) var ( diff --git a/pkg/log/level.go b/log/level.go old mode 100644 new mode 100755 similarity index 93% rename from pkg/log/level.go rename to log/level.go index b0a6d52..8fb827a --- a/pkg/log/level.go +++ b/log/level.go @@ -10,7 +10,6 @@ type Level uint32 const ( SilentLevel Level = iota - FatalLevel ErrorLevel WarnLevel InfoLevel @@ -48,8 +47,6 @@ func (level Level) String() string { return "warning" case ErrorLevel: return "error" - case FatalLevel: - return "fatal" case SilentLevel: return "silent" default: @@ -61,8 +58,6 @@ func ParseLevel(lvl string) (Level, error) { switch strings.ToLower(lvl) { case "silent", "none": return SilentLevel, nil - case "fatal": - return FatalLevel, nil case "error": return ErrorLevel, nil case "warn", "warning": diff --git a/pkg/log/log.go b/log/log.go old mode 100644 new mode 100755 similarity index 95% rename from pkg/log/log.go rename to log/log.go index 7e541d7..d3d6255 --- a/pkg/log/log.go +++ b/log/log.go @@ -8,7 +8,7 @@ import ( ) var ( - // defaultLevel is package default loglevel. + // defaultLevel is package default logging level. defaultLevel = InfoLevel ) diff --git a/main.go b/main.go old mode 100644 new mode 100755 index fd8b463..4f0acfb --- a/main.go +++ b/main.go @@ -1,43 +1,71 @@ package main import ( + "fmt" "os" - "time" + "os/signal" + "runtime" + "syscall" - "github.com/urfave/cli/v2" + "github.com/xjasonlyu/tun2socks/constant" + "github.com/xjasonlyu/tun2socks/engine" + "github.com/xjasonlyu/tun2socks/log" - "github.com/xjasonlyu/tun2socks/internal/cmd" - "github.com/xjasonlyu/tun2socks/pkg/log" + flag "github.com/spf13/pflag" ) -const About = "A tun2socks powered by gVisor TCP/IP stack." - var ( - Version string - BuildTime string + device string + iface string + level string + proxy string + secret string + stats string + mtu int + version bool ) +func init() { + flag.StringVarP(&device, "device", "d", "", "Use this device [driver://]name") + flag.StringVarP(&iface, "interface", "i", "", "Use network INTERFACE (Darwin/Linux only)") + flag.StringVarP(&proxy, "proxy", "p", "", "Use this proxy [protocol://]host[:port]") + flag.StringVarP(&level, "loglevel", "l", "info", "Log level [debug|info|warn|error|silent]") + flag.StringVar(&secret, "secret", "", "HTTP statistic server auth secret") + flag.StringVar(&stats, "stats", "", "HTTP statistic server listen address") + flag.IntVarP(&mtu, "mtu", "m", 0, "Maximum transmission unit") + flag.BoolVarP(&version, "version", "v", false, "Show version information and quit") + flag.Parse() +} + func main() { - app := &cli.App{ - Usage: About, - Version: Version, - Action: cmd.Main, - Flags: []cli.Flag{ - &cmd.API, - &cmd.Device, - &cmd.DNS, - &cmd.Hosts, - &cmd.Interface, - &cmd.LogLevel, - &cmd.Proxy, - &cmd.Version, - }, - HideVersion: true, - HideHelpCommand: true, + if version { + fmt.Printf("%s %s\n%s/%s, %s, %s\n", + constant.Name, + constant.Version, + runtime.GOOS, + runtime.GOARCH, + runtime.Version(), + constant.BuildTime, + ) + os.Exit(0) + } + + options := []engine.Option{ + engine.WithDevice(device), + engine.WithInterface(iface), + engine.WithLogLevel(level), + engine.WithMTU(mtu), + engine.WithProxy(proxy), + engine.WithStats(stats, secret), } - app.Compiled, _ = time.Parse(time.RFC3339, BuildTime) - if err := app.Run(os.Args); err != nil { - log.Fatalf("Failed to start: %v", err) + eng := engine.New(options...) + if err := eng.Start(); err != nil { + log.Fatalf("Start engine error: %v", err) } + defer eng.Stop() + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + <-sigCh } diff --git a/pkg/tun/tun.go b/pkg/tun/tun.go deleted file mode 100644 index d9b3fa5..0000000 --- a/pkg/tun/tun.go +++ /dev/null @@ -1,44 +0,0 @@ -package tun - -import ( - "errors" - "net/url" - "strconv" - "strings" - - "gvisor.dev/gvisor/pkg/tcpip/stack" -) - -const defaultScheme = "tun" - -type Device interface { - stack.LinkEndpoint - - Name() string // returns the current name - Close() error // stops and closes the tun -} - -// Open opens TUN Device with given URL. -func Open(rawURL string) (Device, error) { - if !strings.Contains(rawURL, "://") { - rawURL = defaultScheme + "://" + rawURL - } - - var u *url.URL - u, err := url.Parse(rawURL) - if err != nil { - return nil, err - } - - if strings.ToLower(u.Scheme) != defaultScheme { - return nil, errors.New("unsupported TUN scheme") - } - - var n uint64 - if mtu := u.Query().Get("mtu"); mtu != "" { - n, _ = strconv.ParseUint(mtu, 10, 32) - } - - name := u.Host - return CreateTUN(name, uint32(n)) -} diff --git a/pkg/tun/tun_default.go b/pkg/tun/tun_default.go deleted file mode 100644 index f10aecc..0000000 --- a/pkg/tun/tun_default.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !darwin,!freebsd,!linux,!openbsd - -package tun - -import ( - "fmt" - "runtime" -) - -func CreateTUN(_ string, _ uint32) (Device, error) { - return nil, fmt.Errorf("operation was not supported on %s", runtime.GOOS) -} diff --git a/pkg/tun/tun_linux.go b/pkg/tun/tun_linux.go deleted file mode 100644 index 37d47ec..0000000 --- a/pkg/tun/tun_linux.go +++ /dev/null @@ -1,96 +0,0 @@ -package tun - -import ( - "errors" - "os" - "unsafe" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" - "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" - "gvisor.dev/gvisor/pkg/tcpip/link/tun" - "gvisor.dev/gvisor/pkg/tcpip/stack" -) - -type linuxTun struct { - stack.LinkEndpoint - - tunName string - tunFile *os.File -} - -func CreateTUN(name string, n uint32) (Device, error) { - fd, err := tun.Open(name) - if err != nil { - return nil, err - } - - if n > 0 { - if err := setMTU(name, n); err != nil { - return nil, err - } - } - - var mtu uint32 - if mtu, err = rawfile.GetMTU(name); err != nil { - return nil, err - } - - var ep stack.LinkEndpoint - if ep, err = fdbased.New(&fdbased.Options{ - FDs: []int{fd}, - MTU: mtu, - // TUN only - EthernetHeader: false, - }); err != nil { - return nil, err - } - - return &linuxTun{ - LinkEndpoint: ep, - tunName: name, - tunFile: os.NewFile(uintptr(fd), "tun"), - }, nil -} - -func (t *linuxTun) Name() string { - return t.tunName -} - -func (t *linuxTun) Close() error { - return t.tunFile.Close() -} - -func setMTU(name string, n uint32) error { - // open datagram socket - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - const ifReqSize = unix.IFNAMSIZ + 64 - - // do ioctl call - var ifr [ifReqSize]byte - copy(ifr[:], name) - *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = n - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCSIFMTU), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return errors.New("failed to set MTU of TUN device") - } - - return nil -} diff --git a/pkg/tun/tun_unix.go b/pkg/tun/tun_unix.go deleted file mode 100644 index 3f61bd9..0000000 --- a/pkg/tun/tun_unix.go +++ /dev/null @@ -1,69 +0,0 @@ -// +build darwin freebsd openbsd - -package tun - -import ( - "golang.zx2c4.com/wireguard/tun" - - "github.com/xjasonlyu/clash/common/pool" - "github.com/xjasonlyu/tun2socks/pkg/link/rwc" -) - -const offset = 4 - -type unixTun struct { - *rwc.Endpoint - - device tun.Device -} - -func CreateTUN(name string, n uint32) (Device, error) { - device, err := tun.CreateTUN(name, int(n)) - if err != nil { - return nil, err - } - - mtu, err := device.MTU() - if err != nil { - return nil, err - } - - ut := &unixTun{ - device: device, - } - - if ut.Endpoint, err = rwc.New(ut, uint32(mtu)); err != nil { - return nil, err - } - - return ut, nil -} - -func (t *unixTun) Read(packet []byte) (n int, err error) { - buf := pool.Get(offset + len(packet)) - defer pool.Put(buf) - - if n, err = t.device.Read(buf, offset); err != nil { - return - } - - copy(packet, buf[offset:offset+n]) - return -} - -func (t *unixTun) Write(packet []byte) (int, error) { - buf := pool.Get(offset + len(packet)) - defer pool.Put(buf) - - copy(buf[offset:], packet) - return t.device.Write(buf[:offset+len(packet)], offset) -} - -func (t *unixTun) Name() string { - name, _ := t.device.Name() - return name -} - -func (t *unixTun) Close() error { - return t.device.Close() -} diff --git a/proxy/base.go b/proxy/base.go new file mode 100755 index 0000000..90a9396 --- /dev/null +++ b/proxy/base.go @@ -0,0 +1,29 @@ +package proxy + +import ( + "context" + "errors" + "net" + + "github.com/xjasonlyu/tun2socks/common/adapter" +) + +type Base struct { + addr string +} + +func NewBase(addr string) *Base { + return &Base{addr: addr} +} + +func (b *Base) Addr() string { + return b.addr +} + +func (b *Base) DialContext(context.Context, *adapter.Metadata) (net.Conn, error) { + return nil, errors.New("not supported") +} + +func (b *Base) DialUDP(*adapter.Metadata) (net.PacketConn, error) { + return nil, errors.New("not supported") +} diff --git a/internal/proxy/direct.go b/proxy/direct.go similarity index 56% rename from internal/proxy/direct.go rename to proxy/direct.go index f8730f8..6960c24 100755 --- a/internal/proxy/direct.go +++ b/proxy/direct.go @@ -3,22 +3,17 @@ package proxy import ( "context" "net" - "net/url" - "github.com/xjasonlyu/clash/component/dialer" - "github.com/xjasonlyu/tun2socks/internal/adapter" + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/component/dialer" ) type Direct struct { *Base } -func NewDirect(url *url.URL) (*Direct, error) { - return &Direct{ - Base: &Base{ - url: url, - }, - }, nil +func NewDirect() *Direct { + return &Direct{} } func (d *Direct) DialContext(ctx context.Context, metadata *adapter.Metadata) (net.Conn, error) { @@ -26,7 +21,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *adapter.Metadata) (n if err != nil { return nil, err } - tcpKeepAlive(c) + setKeepAlive(c) return c, nil } @@ -42,14 +37,12 @@ type directPacketConn struct { net.PacketConn } -func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (_ int, err error) { - var udpAddr *net.UDPAddr - if m, ok := addr.(*adapter.Metadata); ok && m.Host == "" { - udpAddr = m.UDPAddr() - } else { - udpAddr, err = resolveUDPAddr("udp", addr.String()) +func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + if m, ok := addr.(*adapter.Metadata); ok && m.DstIP != nil { + return pc.PacketConn.WriteTo(b, m.UDPAddr()) } + udpAddr, err := net.ResolveUDPAddr("udp", addr.String()) if err != nil { return 0, err } diff --git a/proxy/keepalive.go b/proxy/keepalive.go new file mode 100755 index 0000000..6a986e6 --- /dev/null +++ b/proxy/keepalive.go @@ -0,0 +1,18 @@ +package proxy + +import ( + "net" + "time" +) + +const ( + tcpKeepAlivePeriod = 30 * time.Second +) + +// setKeepAlive sets tcp keepalive option for tcp connection. +func setKeepAlive(c net.Conn) { + if tcp, ok := c.(*net.TCPConn); ok { + tcp.SetKeepAlive(true) + tcp.SetKeepAlivePeriod(tcpKeepAlivePeriod) + } +} diff --git a/proxy/proxy.go b/proxy/proxy.go new file mode 100755 index 0000000..d9fb465 --- /dev/null +++ b/proxy/proxy.go @@ -0,0 +1,45 @@ +// Package proxy provides implementations of proxy protocols. +package proxy + +import ( + "context" + "net" + "time" + + "github.com/xjasonlyu/tun2socks/common/adapter" +) + +const ( + tcpConnectTimeout = 5 * time.Second +) + +var ( + _defaultDialer Dialer = &Base{} +) + +type Dialer interface { + DialContext(context.Context, *adapter.Metadata) (net.Conn, error) + DialUDP(*adapter.Metadata) (net.PacketConn, error) +} + +// SetDialer sets default Dialer. +func SetDialer(d Dialer) { + _defaultDialer = d +} + +// Dial uses default Dialer to dial TCP. +func Dial(metadata *adapter.Metadata) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout) + defer cancel() + return _defaultDialer.DialContext(ctx, metadata) +} + +// DialContext uses default Dialer to dial TCP with context. +func DialContext(ctx context.Context, metadata *adapter.Metadata) (net.Conn, error) { + return _defaultDialer.DialContext(ctx, metadata) +} + +// DialUDP uses default Dialer to dial UDP. +func DialUDP(metadata *adapter.Metadata) (net.PacketConn, error) { + return _defaultDialer.DialUDP(metadata) +} diff --git a/internal/proxy/shadowsocks.go b/proxy/shadowsocks.go similarity index 74% rename from internal/proxy/shadowsocks.go rename to proxy/shadowsocks.go index 5ad0191..559faf5 100755 --- a/internal/proxy/shadowsocks.go +++ b/proxy/shadowsocks.go @@ -5,13 +5,12 @@ import ( "errors" "fmt" "net" - "net/url" + + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/component/dialer" + "github.com/xjasonlyu/tun2socks/component/socks5" "github.com/Dreamacro/go-shadowsocks2/core" - - "github.com/xjasonlyu/clash/component/dialer" - "github.com/xjasonlyu/clash/component/socks5" - "github.com/xjasonlyu/tun2socks/internal/adapter" ) type ShadowSocks struct { @@ -20,16 +19,14 @@ type ShadowSocks struct { cipher core.Cipher } -func NewShadowSocks(url *url.URL, method, password string) (*ShadowSocks, error) { +func NewShadowSocks(addr, method, password string) (*ShadowSocks, error) { cipher, err := core.PickCipher(method, nil, password) if err != nil { return nil, fmt.Errorf("ss initialize: %w", err) } return &ShadowSocks{ - Base: &Base{ - url: url, - }, + Base: NewBase(addr), cipher: cipher, }, nil } @@ -39,10 +36,10 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *adapter.Metada if err != nil { return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err) } - tcpKeepAlive(c) + setKeepAlive(c) defer func() { - if err != nil { + if err != nil && c != nil { c.Close() } }() @@ -55,12 +52,12 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *adapter.Metada func (ss *ShadowSocks) DialUDP(_ *adapter.Metadata) (net.PacketConn, error) { pc, err := dialer.ListenPacket("udp", "") if err != nil { - return nil, err + return nil, fmt.Errorf("listen packet: %w", err) } - udpAddr, err := resolveUDPAddr("udp", ss.Addr()) + udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr()) if err != nil { - return nil, fmt.Errorf("resolve UDPAddr %s failed", ss.Addr()) + return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err) } pc = ss.cipher.PacketConn(pc) @@ -88,9 +85,9 @@ func (pc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { } func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { - n, _, e := pc.PacketConn.ReadFrom(b) - if e != nil { - return 0, nil, e + n, _, err := pc.PacketConn.ReadFrom(b) + if err != nil { + return 0, nil, err } addr := socks5.SplitAddr(b[:n]) @@ -104,5 +101,5 @@ func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { } copy(b, b[len(addr):]) - return n - len(addr), udpAddr, e + return n - len(addr), udpAddr, err } diff --git a/internal/proxy/socks5.go b/proxy/socks5.go similarity index 81% rename from internal/proxy/socks5.go rename to proxy/socks5.go index 7f2f485..8afa900 100755 --- a/internal/proxy/socks5.go +++ b/proxy/socks5.go @@ -6,11 +6,10 @@ import ( "io" "io/ioutil" "net" - "net/url" - "github.com/xjasonlyu/clash/component/dialer" - "github.com/xjasonlyu/clash/component/socks5" - "github.com/xjasonlyu/tun2socks/internal/adapter" + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/component/dialer" + "github.com/xjasonlyu/tun2socks/component/socks5" ) type Socks5 struct { @@ -20,11 +19,9 @@ type Socks5 struct { pass string } -func NewSocks5(url *url.URL, user, pass string) (*Socks5, error) { +func NewSocks5(addr, user, pass string) (*Socks5, error) { return &Socks5{ - Base: &Base{ - url: url, - }, + Base: NewBase(addr), user: user, pass: pass, }, nil @@ -35,10 +32,10 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *adapter.Metadata) ( if err != nil { return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err) } - tcpKeepAlive(c) + setKeepAlive(c) defer func() { - if err != nil { + if err != nil && c != nil { c.Close() } }() @@ -58,15 +55,16 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *adapter.Metadata) ( func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) { ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout) defer cancel() + c, err := dialer.DialContext(ctx, "tcp", ss.Addr()) if err != nil { err = fmt.Errorf("connect to %s: %w", ss.Addr(), err) return } - tcpKeepAlive(c) + setKeepAlive(c) defer func() { - if err != nil { + if err != nil && c != nil { c.Close() } }() @@ -96,7 +94,7 @@ func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) { pc, err := dialer.ListenPacket("udp", "") if err != nil { - return + return nil, fmt.Errorf("listen packet: %w", err) } go func() { @@ -108,12 +106,11 @@ func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) { }() bindAddr := addr.UDPAddr() - if bindAddr.IP.IsUnspecified() { - udpAddr, err := resolveUDPAddr("udp", ss.Addr()) + if bindAddr.IP.IsUnspecified() { /* e.g. "0.0.0.0" or "::" */ + udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr()) if err != nil { - return nil, err + return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err) } - bindAddr.IP = udpAddr.IP } @@ -142,10 +139,11 @@ func (pc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { } func (pc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { - n, _, e := pc.PacketConn.ReadFrom(b) - if e != nil { - return 0, nil, e + n, _, err := pc.PacketConn.ReadFrom(b) + if err != nil { + return 0, nil, err } + addr, payload, err := socks5.DecodeUDPPacket(b) if err != nil { return 0, nil, err @@ -153,7 +151,7 @@ func (pc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { udpAddr := addr.UDPAddr() if udpAddr == nil { - return 0, nil, fmt.Errorf("convert %s to UDPAddr failed", addr) + return 0, nil, fmt.Errorf("convert %s to UDPAddr is nil", addr) } // due to DecodeUDPPacket is mutable, record addr length diff --git a/stack/handler.go b/stack/handler.go new file mode 100755 index 0000000..f4219c2 --- /dev/null +++ b/stack/handler.go @@ -0,0 +1,10 @@ +package stack + +import ( + "github.com/xjasonlyu/tun2socks/common/adapter" +) + +type Handler interface { + Add(adapter.TCPConn) + AddPacket(adapter.UDPPacket) +} diff --git a/internal/core/icmp.go b/stack/icmp.go old mode 100644 new mode 100755 similarity index 57% rename from internal/core/icmp.go rename to stack/icmp.go index 682d2ff..51d8556 --- a/internal/core/icmp.go +++ b/stack/icmp.go @@ -1,27 +1,24 @@ -package core +package stack import ( "fmt" "net" "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/stack" ) -type icmpHandleFunc func() - -func WithICMPHandler(_ icmpHandleFunc) Option { - return func(s *stack.Stack) error { - // Add default route table for IPv4 and IPv6 +func withICMPHandler() Option { + return func(s *Stack) error { + // Add default route table for IPv4 and IPv6. // This will handle all incoming ICMP packets. s.SetRouteTable([]tcpip.Route{ { Destination: mustSubnet("0.0.0.0/0"), - NIC: defaultNICID, + NIC: s.nicID, }, { Destination: mustSubnet("::/0"), - NIC: defaultNICID, + NIC: s.nicID, }, }) return nil @@ -32,12 +29,12 @@ func WithICMPHandler(_ icmpHandleFunc) Option { func mustSubnet(s string) tcpip.Subnet { _, ipNet, err := net.ParseCIDR(s) if err != nil { - panic(fmt.Errorf("unable to ParseCIDR(%s): %w", s, err)) + panic(fmt.Errorf("parse CIDR: %w", err)) } subnet, err := tcpip.NewSubnet(tcpip.Address(ipNet.IP), tcpip.AddressMask(ipNet.Mask)) if err != nil { - panic(fmt.Errorf("unable to NewSubnet(%s): %w", ipNet, err)) + panic(fmt.Errorf("new subnet: %w", err)) } return subnet } diff --git a/internal/core/nic.go b/stack/nic.go old mode 100644 new mode 100755 similarity index 50% rename from internal/core/nic.go rename to stack/nic.go index 4ce62f2..5c4ec73 --- a/internal/core/nic.go +++ b/stack/nic.go @@ -1,4 +1,4 @@ -package core +package stack import ( "fmt" @@ -9,7 +9,7 @@ import ( const ( // defaultNICID is the ID of default NIC used by DefaultStack. - defaultNICID tcpip.NICID = 1 + defaultNICID tcpip.NICID = 0x01 // nicPromiscuousModeEnabled is the value used by stack to enable // or disable NIC's promiscuous mode. @@ -20,31 +20,31 @@ const ( nicSpoofingEnabled = true ) -// WithCreatingNIC creates NIC for stack. -func WithCreatingNIC(nicID tcpip.NICID, ep stack.LinkEndpoint) Option { - return func(s *stack.Stack) error { - if err := s.CreateNIC(nicID, ep); err != nil { +// withCreatingNIC creates NIC for stack. +func withCreatingNIC(ep stack.LinkEndpoint) Option { + return func(s *Stack) error { + if err := s.CreateNIC(s.nicID, ep); err != nil { return fmt.Errorf("create NIC: %s", err) } return nil } } -// WithPromiscuousMode sets promiscuous mode in the given NIC. -func WithPromiscuousMode(nicID tcpip.NICID, v bool) Option { - return func(s *stack.Stack) error { - if err := s.SetPromiscuousMode(nicID, v); err != nil { +// withPromiscuousMode sets promiscuous mode in the given NIC. +func withPromiscuousMode(v bool) Option { + return func(s *Stack) error { + if err := s.SetPromiscuousMode(s.nicID, v); err != nil { return fmt.Errorf("set promiscuous mode: %s", err) } return nil } } -// WithSpoofing sets address spoofing in the given NIC, allowing +// withSpoofing sets address spoofing in the given NIC, allowing // endpoints to bind to any address in the NIC. -func WithSpoofing(nicID tcpip.NICID, v bool) Option { - return func(s *stack.Stack) error { - if err := s.SetSpoofing(nicID, v); err != nil { +func withSpoofing(v bool) Option { + return func(s *Stack) error { + if err := s.SetSpoofing(s.nicID, v); err != nil { return fmt.Errorf("set spoofing: %s", err) } return nil diff --git a/internal/core/opts.go b/stack/opts.go old mode 100644 new mode 100755 similarity index 76% rename from internal/core/opts.go rename to stack/opts.go index 2e80cea..a16f700 --- a/internal/core/opts.go +++ b/stack/opts.go @@ -1,4 +1,4 @@ -package core +package stack import ( "fmt" @@ -7,7 +7,6 @@ import ( "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" - "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" ) @@ -54,11 +53,50 @@ const ( tcpSACKEnabled = true ) -type Option func(*stack.Stack) error +type Option func(*Stack) error + +// WithDefault sets all default values for stack. +func WithDefault() Option { + return func(s *Stack) error { + opts := []Option{ + WithDefaultTTL(defaultTimeToLive), + WithForwarding(ipForwardingEnabled), + + // Config default stack ICMP settings. + WithICMPBurst(icmpBurst), WithICMPLimit(icmpLimit), + + // We expect no packet loss, therefore we can bump buffers. + // Too large buffers thrash cache, so there is little point + // in too large buffers. + // + // Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go + WithTCPBufferSizeRange(minBufferSize, defaultBufferSize, maxBufferSize), + + WithTCPCongestionControl(tcpCongestionControlAlgorithm), + WithTCPDelay(tcpDelayEnabled), + + // Receive Buffer Auto-Tuning Option, see: + // https://github.com/google/gvisor/issues/1666 + WithTCPModerateReceiveBuffer(tcpModerateReceiveBufferEnabled), + + // TCP selective ACK Option, see: + // https://tools.ietf.org/html/rfc2018 + WithTCPSACKEnabled(tcpSACKEnabled), + } + + for _, opt := range opts { + if err := opt(s); err != nil { + return err + } + } + + return nil + } +} // WithDefaultTTL sets the default TTL used by stack. func WithDefaultTTL(ttl uint8) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { opt := tcpip.DefaultTTLOption(ttl) if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil { return fmt.Errorf("set ipv4 default TTL: %s", err) @@ -72,7 +110,7 @@ func WithDefaultTTL(ttl uint8) Option { // WithForwarding sets packet forwarding between NICs for IPv4 & IPv6. func WithForwarding(v bool) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { if err := s.SetForwarding(ipv4.ProtocolNumber, v); err != nil { return fmt.Errorf("set ipv4 forwarding: %s", err) } @@ -86,7 +124,7 @@ func WithForwarding(v bool) Option { // WithICMPBurst sets the number of ICMP messages that can be sent // in a single burst. func WithICMPBurst(burst int) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { s.SetICMPBurst(burst) return nil } @@ -95,7 +133,7 @@ func WithICMPBurst(burst int) Option { // WithICMPLimit sets the maximum number of ICMP messages permitted // by rate limiter. func WithICMPLimit(limit rate.Limit) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { s.SetICMPLimit(limit) return nil } @@ -103,7 +141,7 @@ func WithICMPLimit(limit rate.Limit) Option { // WithTCPBufferSizeRange sets the receive and send buffer size range for TCP. func WithTCPBufferSizeRange(a, b, c int) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c} if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil { return fmt.Errorf("set TCP receive buffer size range: %s", err) @@ -118,7 +156,7 @@ func WithTCPBufferSizeRange(a, b, c int) Option { // WithTCPCongestionControl sets the current congestion control algorithm. func WithTCPCongestionControl(cc string) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { opt := tcpip.CongestionControlOption(cc) if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { return fmt.Errorf("set TCP congestion control algorithm: %s", err) @@ -129,7 +167,7 @@ func WithTCPCongestionControl(cc string) Option { // WithTCPDelay enables or disables Nagle's algorithm in TCP. func WithTCPDelay(v bool) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { opt := tcpip.TCPDelayEnabled(v) if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { return fmt.Errorf("set TCP delay: %s", err) @@ -140,7 +178,7 @@ func WithTCPDelay(v bool) Option { // WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP. func WithTCPModerateReceiveBuffer(v bool) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { opt := tcpip.TCPModerateReceiveBufferOption(v) if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { return fmt.Errorf("set TCP moderate receive buffer: %s", err) @@ -151,7 +189,7 @@ func WithTCPModerateReceiveBuffer(v bool) Option { // WithTCPSACKEnabled sets the SACK option for TCP. func WithTCPSACKEnabled(v bool) Option { - return func(s *stack.Stack) error { + return func(s *Stack) error { opt := tcpip.TCPSACKEnabled(v) if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { return fmt.Errorf("set TCP SACK: %s", err) diff --git a/stack/stack.go b/stack/stack.go new file mode 100755 index 0000000..629c774 --- /dev/null +++ b/stack/stack.go @@ -0,0 +1,80 @@ +// Package stack provides a thin wrapper around a gVisor's stack. +package stack + +import ( + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +type Stack struct { + *stack.Stack + + handler Handler + nicID tcpip.NICID +} + +// New allocates a new *Stack with given options. +func New(ep stack.LinkEndpoint, handler Handler, opts ...Option) (*Stack, error) { + s := &Stack{ + Stack: stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ + ipv4.NewProtocol, + ipv6.NewProtocol, + }, + TransportProtocols: []stack.TransportProtocolFactory{ + tcp.NewProtocol, + udp.NewProtocol, + icmp.NewProtocol4, + icmp.NewProtocol6, + }, + }), + + handler: handler, + nicID: defaultNICID, + } + + opts = append(opts, + // Important: We must initiate transport protocol handlers + // before creating NIC, otherwise NIC would dispatch packets + // to stack and cause race condition. + withICMPHandler(), withTCPHandler(), withUDPHandler(), + + // Create stack NIC and then bind link endpoint. + withCreatingNIC(ep), + + // In past we did s.AddAddressRange to assign 0.0.0.0/0 onto + // the interface. We need that to be able to terminate all the + // incoming connections - to any ip. AddressRange API has been + // removed and the suggested workaround is to use Promiscuous + // mode. https://github.com/google/gvisor/issues/3876 + // + // Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go + withPromiscuousMode(nicPromiscuousModeEnabled), + + // Enable spoofing if a stack may send packets from unowned addresses. + // This change required changes to some netgophers since previously, + // promiscuous mode was enough to let the netstack respond to all + // incoming packets regardless of the packet's destination address. Now + // that a stack.Route is not held for each incoming packet, finding a route + // may fail with local addresses we don't own but accepted packets for + // while in promiscuous mode. Since we also want to be able to send from + // any address (in response the received promiscuous mode packets), we need + // to enable spoofing. + // + // Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127 + withSpoofing(nicSpoofingEnabled), + ) + + for _, opt := range opts { + if err := opt(s); err != nil { + return nil, err + } + } + + return s, nil +} diff --git a/internal/core/tcp.go b/stack/tcp.go old mode 100644 new mode 100755 similarity index 60% rename from internal/core/tcp.go rename to stack/tcp.go index a6b048f..d13b4c0 --- a/internal/core/tcp.go +++ b/stack/tcp.go @@ -1,18 +1,16 @@ -package core +package stack import ( "fmt" "net" "time" + "github.com/xjasonlyu/tun2socks/common/adapter" + "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" - "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/waiter" - - "github.com/xjasonlyu/tun2socks/internal/adapter" - "github.com/xjasonlyu/tun2socks/pkg/log" ) const ( @@ -35,25 +33,20 @@ const ( tcpKeepaliveInterval = 30 * time.Second ) -type tcpHandleFunc func(adapter.TCPConn) - -func WithTCPHandler(handle tcpHandleFunc) Option { - return func(s *stack.Stack) error { - tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) { +func withTCPHandler() Option { + return func(s *Stack) error { + tcpForwarder := tcp.NewForwarder(s.Stack, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) { var wq waiter.Queue id := r.ID() ep, err := r.CreateEndpoint(&wq) if err != nil { - log.Warnf("[STACK] %s create endpoint error: %v", formatID(&id), err) // prevent potential half-open TCP connection leak. r.Complete(true) return } r.Complete(false) - if err := setKeepalive(ep); err != nil { - log.Warnf("[STACK] %s %v", formatID(&id), err) - } + setKeepalive(ep) conn := &tcpConn{ Conn: gonet.NewTCPConn(&wq, ep), @@ -66,33 +59,23 @@ func WithTCPHandler(handle tcpHandleFunc) Option { }, } - handle(conn) + s.handler.Add(conn) }) s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) return nil } } -func formatID(id *stack.TransportEndpointID) string { - return fmt.Sprintf( - "%s:%d --> %s:%d", - id.RemoteAddress, - id.RemotePort, - id.LocalAddress, - id.LocalPort, - ) -} - func setKeepalive(ep tcpip.Endpoint) error { - if err := ep.SetSockOptBool(tcpip.KeepaliveEnabledOption, true); err != nil { - return fmt.Errorf("set keepalive: %s", err) - } - idleOpt := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle) - if err := ep.SetSockOpt(&idleOpt); err != nil { + ep.SocketOptions().SetKeepAlive(true) + + idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle) + if err := ep.SetSockOpt(&idle); err != nil { return fmt.Errorf("set keepalive idle: %s", err) } - intervalOpt := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval) - if err := ep.SetSockOpt(&intervalOpt); err != nil { + + interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval) + if err := ep.SetSockOpt(&interval); err != nil { return fmt.Errorf("set keepalive interval: %s", err) } return nil diff --git a/stack/udp.go b/stack/udp.go new file mode 100755 index 0000000..0324be6 --- /dev/null +++ b/stack/udp.go @@ -0,0 +1,196 @@ +package stack + +import ( + "fmt" + "net" + + "github.com/xjasonlyu/tun2socks/common/adapter" + + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +const ( + // udpNoChecksum disables UDP checksum. + udpNoChecksum = true +) + +func withUDPHandler() Option { + return func(s *Stack) error { + udpHandlePacket := func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool { + // Ref: gVisor pkg/tcpip/transport/udp/endpoint.go HandlePacket + udpHdr := header.UDP(pkt.TransportHeader().View()) + if int(udpHdr.Length()) > pkt.Data.Size()+header.UDPMinimumSize { + // Malformed packet. + s.Stats().UDP.MalformedPacketsReceived.Increment() + return true + } + + if !verifyChecksum(udpHdr, pkt) { + // Checksum error. + s.Stats().UDP.ChecksumErrors.Increment() + return true + } + + s.Stats().UDP.PacketsReceived.Increment() + + packet := &udpPacket{ + s: s, + id: id, + nicID: pkt.NICID, + netHdr: pkt.Network(), + netProto: pkt.NetworkProtocolNumber, + payload: pkt.Data.ToView(), + metadata: &adapter.Metadata{ + Net: adapter.UDP, + SrcIP: net.IP(id.RemoteAddress), + SrcPort: id.RemotePort, + DstIP: net.IP(id.LocalAddress), + DstPort: id.LocalPort, + }, + } + + s.handler.AddPacket(packet) + return true + } + s.SetTransportProtocolHandler(udp.ProtocolNumber, udpHandlePacket) + return nil + } +} + +type udpPacket struct { + s *Stack + id stack.TransportEndpointID + nicID tcpip.NICID + netHdr header.Network + netProto tcpip.NetworkProtocolNumber + payload []byte + metadata *adapter.Metadata +} + +func (p *udpPacket) Data() []byte { + return p.payload +} + +func (p *udpPacket) Drop() { + /* Release */ +} + +func (p *udpPacket) LocalAddr() net.Addr { + return &net.UDPAddr{IP: net.IP(p.id.LocalAddress), Port: int(p.id.LocalPort)} +} + +func (p *udpPacket) Metadata() *adapter.Metadata { + return p.metadata +} + +func (p *udpPacket) RemoteAddr() net.Addr { + return &net.UDPAddr{IP: net.IP(p.id.RemoteAddress), Port: int(p.id.RemotePort)} +} + +func (p *udpPacket) WriteBack(b []byte, addr net.Addr) (int, error) { + v := buffer.View(b) + if len(v) > header.UDPMaximumPacketSize { + // Payload can't possibly fit in a packet. + return 0, fmt.Errorf("%s", &tcpip.ErrMessageTooLong{}) + } + + route, err := p.s.FindRoute(p.nicID, p.netHdr.DestinationAddress(), p.netHdr.SourceAddress(), p.netProto, false /* multicastLoop */) + if err != nil { + return 0, fmt.Errorf("%#v find route: %s", p.id, err) + } + defer route.Release() + + data := v.ToVectorisedView() + // if addr is not provided, write back use original dst Addr as src Addr. + if addr == nil { + if err := sendUDP(route, data, p.id.LocalPort, p.id.RemotePort, udpNoChecksum); err != nil { + return 0, fmt.Errorf("%v", err) + } + return data.Size(), nil + } + + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + return 0, fmt.Errorf("type %T is not a valid udp address", addr) + } + + if ipv4 := udpAddr.IP.To4(); ipv4 != nil { + route.LocalAddress = tcpip.Address(ipv4) + } else { + route.LocalAddress = tcpip.Address(udpAddr.IP) + } + + if err := sendUDP(route, data, uint16(udpAddr.Port), p.id.RemotePort, udpNoChecksum); err != nil { + return 0, fmt.Errorf("%v", err) + } + return data.Size(), nil +} + +// sendUDP sends a UDP segment via the provided network endpoint and under the +// provided identity. +func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, noChecksum bool) tcpip.Error { + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), + Data: data, + }) + + // Initialize the UDP header. + udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize)) + pkt.TransportProtocolNumber = udp.ProtocolNumber + + length := uint16(pkt.Size()) + udpHdr.Encode(&header.UDPFields{ + SrcPort: localPort, + DstPort: remotePort, + Length: length, + }) + + // Set the checksum field unless TX checksum offload is enabled. + // On IPv4, UDP checksum is optional, and a zero value indicates the + // transmitter skipped the checksum generation (RFC768). + // On IPv6, UDP checksum is not optional (RFC2460 Section 8.1). + if r.RequiresTXTransportChecksum() && + (!noChecksum || r.NetProto == header.IPv6ProtocolNumber) { + xsum := r.PseudoHeaderChecksum(udp.ProtocolNumber, length) + for _, v := range data.Views() { + xsum = header.Checksum(v, xsum) + } + udpHdr.SetChecksum(^udpHdr.CalculateChecksum(xsum)) + } + + ttl := r.DefaultTTL() + + if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{ + Protocol: udp.ProtocolNumber, + TTL: ttl, + TOS: 0, /* default */ + }, pkt); err != nil { + r.Stats().UDP.PacketSendErrors.Increment() + return err + } + + // Track count of packets sent. + r.Stats().UDP.PacketsSent.Increment() + return nil +} + +// verifyChecksum verifies the checksum unless RX checksum offload is enabled. +// On IPv4, UDP checksum is optional, and a zero value means the transmitter +// omitted the checksum generation (RFC768). +// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1). +func verifyChecksum(hdr header.UDP, pkt *stack.PacketBuffer) bool { + if !pkt.RXTransportChecksumValidated && + (hdr.Checksum() != 0 || pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber) { + netHdr := pkt.Network() + xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, netHdr.DestinationAddress(), netHdr.SourceAddress(), hdr.Length()) + for _, v := range pkt.Data.Views() { + xsum = header.Checksum(v, xsum) + } + return hdr.CalculateChecksum(xsum) == 0xffff + } + return true +} diff --git a/internal/api/connections.go b/stats/connections.go old mode 100644 new mode 100755 similarity index 84% rename from internal/api/connections.go rename to stats/connections.go index a35e5c8..d25f08c --- a/internal/api/connections.go +++ b/stats/connections.go @@ -1,4 +1,4 @@ -package api +package stats import ( "bytes" @@ -7,13 +7,15 @@ import ( "strconv" "time" + "github.com/xjasonlyu/tun2socks/tunnel" + "github.com/go-chi/chi" "github.com/go-chi/render" "github.com/gorilla/websocket" - - "github.com/xjasonlyu/tun2socks/internal/manager" ) +const defaultInterval = 1000 + func connectionRouter() http.Handler { r := chi.NewRouter() r.Get("/", getConnections) @@ -24,7 +26,7 @@ func connectionRouter() http.Handler { func getConnections(w http.ResponseWriter, r *http.Request) { if !websocket.IsWebSocketUpgrade(r) { - snapshot := manager.DefaultManager.Snapshot() + snapshot := tunnel.DefaultManager.Snapshot() render.JSON(w, r, snapshot) return } @@ -35,7 +37,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) { } intervalStr := r.URL.Query().Get("interval") - interval := 1000 + interval := defaultInterval if intervalStr != "" { t, err := strconv.Atoi(intervalStr) if err != nil { @@ -50,7 +52,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) { buf := &bytes.Buffer{} sendSnapshot := func() error { buf.Reset() - snapshot := manager.DefaultManager.Snapshot() + snapshot := tunnel.DefaultManager.Snapshot() if err := json.NewEncoder(buf).Encode(snapshot); err != nil { return err } @@ -73,7 +75,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) { func closeConnection(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - snapshot := manager.DefaultManager.Snapshot() + snapshot := tunnel.DefaultManager.Snapshot() for _, c := range snapshot.Connections { if id == c.ID() { _ = c.Close() @@ -84,7 +86,7 @@ func closeConnection(w http.ResponseWriter, r *http.Request) { } func closeAllConnections(w http.ResponseWriter, r *http.Request) { - snapshot := manager.DefaultManager.Snapshot() + snapshot := tunnel.DefaultManager.Snapshot() for _, c := range snapshot.Connections { _ = c.Close() } diff --git a/stats/errors.go b/stats/errors.go new file mode 100755 index 0000000..614cfc8 --- /dev/null +++ b/stats/errors.go @@ -0,0 +1,21 @@ +package stats + +var ( + ErrUnauthorized = newError("Unauthorized") + ErrBadRequest = newError("Body invalid") +) + +var _ error = (*HTTPError)(nil) + +// HTTPError is custom HTTP error for API +type HTTPError struct { + Message string `json:"message"` +} + +func (e *HTTPError) Error() string { + return e.Message +} + +func newError(msg string) *HTTPError { + return &HTTPError{Message: msg} +} diff --git a/internal/api/server.go b/stats/server.go old mode 100644 new mode 100755 similarity index 64% rename from internal/api/server.go rename to stats/server.go index d9c5cce..f1d69d6 --- a/internal/api/server.go +++ b/stats/server.go @@ -1,32 +1,24 @@ -package api +package stats import ( "bytes" "encoding/json" - "errors" "net" "net/http" - "net/url" "strings" "time" + "github.com/xjasonlyu/tun2socks/constant" + "github.com/xjasonlyu/tun2socks/log" + "github.com/xjasonlyu/tun2socks/tunnel" + "github.com/go-chi/chi" "github.com/go-chi/cors" "github.com/go-chi/render" "github.com/gorilla/websocket" - "github.com/urfave/cli/v2" - - "github.com/xjasonlyu/tun2socks/internal/manager" - "github.com/xjasonlyu/tun2socks/pkg/log" ) -const defaultScheme = "api" - var ( - serverApp *cli.App - serverSecret = "" - serverAddr = "" - upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true @@ -34,23 +26,7 @@ var ( } ) -func Start(apiURL string, app *cli.App) error { - if !strings.Contains(apiURL, "://") { - apiURL = defaultScheme + "://" + apiURL - } - - u, err := url.Parse(apiURL) - if err != nil { - return err - } - - if strings.ToLower(u.Scheme) != defaultScheme { - return errors.New("unsupported scheme") - } - serverApp = app - serverAddr = u.Host - serverSecret = u.Query().Get("secret") - +func Start(addr, secret string) error { r := chi.NewRouter() c := cors.New(cors.Options{ @@ -61,10 +37,8 @@ func Start(apiURL string, app *cli.App) error { }) r.Use(c.Handler) - r.Group(func(r chi.Router) { - r.Use(authentication) - + r.Use(authenticator(secret)) r.Get("/", hello) r.Get("/logs", getLogs) r.Get("/traffic", traffic) @@ -72,7 +46,7 @@ func Start(apiURL string, app *cli.App) error { r.Mount("/connections", connectionRouter()) }) - tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddr) + tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return err } @@ -82,45 +56,47 @@ func Start(apiURL string, app *cli.App) error { return err } - go func() { - _ = http.Serve(listener, r) - }() - - return nil + return http.Serve(listener, r) } -func authentication(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if serverSecret == "" { - next.ServeHTTP(w, r) - return - } +func hello(w http.ResponseWriter, r *http.Request) { + render.JSON(w, r, render.M{"hello": constant.Name}) +} - // Browser websocket not support custom header - if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" { - token := r.URL.Query().Get("token") - if token != serverSecret { +func authenticator(secret string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if secret == "" { + next.ServeHTTP(w, r) + return + } + + // Browser websocket not support custom header + if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" { + token := r.URL.Query().Get("token") + if token != secret { + render.Status(r, http.StatusUnauthorized) + render.JSON(w, r, ErrUnauthorized) + return + } + next.ServeHTTP(w, r) + return + } + + header := r.Header.Get("Authorization") + text := strings.SplitN(header, " ", 2) + + hasInvalidHeader := text[0] != "Bearer" + hasInvalidSecret := len(text) != 2 || text[1] != secret + if hasInvalidHeader || hasInvalidSecret { render.Status(r, http.StatusUnauthorized) render.JSON(w, r, ErrUnauthorized) return } next.ServeHTTP(w, r) - return } - - header := r.Header.Get("Authorization") - text := strings.SplitN(header, " ", 2) - - hasInvalidHeader := text[0] != "Bearer" - hasInvalidSecret := len(text) != 2 || text[1] != serverSecret - if hasInvalidHeader || hasInvalidSecret { - render.Status(r, http.StatusUnauthorized) - render.JSON(w, r, ErrUnauthorized) - return - } - next.ServeHTTP(w, r) + return http.HandlerFunc(fn) } - return http.HandlerFunc(fn) } func getLogs(w http.ResponseWriter, r *http.Request) { @@ -178,10 +154,6 @@ func getLogs(w http.ResponseWriter, r *http.Request) { } } -func hello(w http.ResponseWriter, r *http.Request) { - render.JSON(w, r, render.M{"hello": serverApp.Name}) -} - type Traffic struct { Up int64 `json:"up"` Down int64 `json:"down"` @@ -204,7 +176,7 @@ func traffic(w http.ResponseWriter, r *http.Request) { tick := time.NewTicker(time.Second) defer tick.Stop() - t := manager.DefaultManager + t := tunnel.DefaultManager buf := &bytes.Buffer{} var err error for range tick.C { @@ -231,5 +203,5 @@ func traffic(w http.ResponseWriter, r *http.Request) { } func version(w http.ResponseWriter, r *http.Request) { - render.JSON(w, r, render.M{"version": serverApp.Version}) + render.JSON(w, r, render.M{"version": constant.Version}) } diff --git a/stats/stats.go b/stats/stats.go new file mode 100644 index 0000000..cd552d4 --- /dev/null +++ b/stats/stats.go @@ -0,0 +1,4 @@ +// Package stats provides statistic data via http server. +package stats + +// Ref: github.com/Dreamacro/clash/hub/route diff --git a/tunnel/manager.go b/tunnel/manager.go new file mode 100755 index 0000000..261e685 --- /dev/null +++ b/tunnel/manager.go @@ -0,0 +1,22 @@ +package tunnel + +import ( + "net" + + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/component/manager" +) + +var ( + // DefaultManager is the default traffic and connections + // manager used by tunnel. + DefaultManager = manager.New() +) + +func newTCPTracker(conn net.Conn, metadata *adapter.Metadata) net.Conn { + return manager.NewTCPTracker(conn, metadata, DefaultManager) +} + +func newUDPTracker(conn net.PacketConn, metadata *adapter.Metadata) net.PacketConn { + return manager.NewUDPTracker(conn, metadata, DefaultManager) +} diff --git a/internal/tunnel/tcp.go b/tunnel/tcp.go old mode 100644 new mode 100755 similarity index 67% rename from internal/tunnel/tcp.go rename to tunnel/tcp.go index 539eb4d..51ebb28 --- a/internal/tunnel/tcp.go +++ b/tunnel/tcp.go @@ -7,16 +7,14 @@ import ( "sync" "time" - "github.com/xjasonlyu/clash/common/pool" - "github.com/xjasonlyu/tun2socks/internal/adapter" - "github.com/xjasonlyu/tun2socks/internal/manager" - "github.com/xjasonlyu/tun2socks/internal/proxy" - "github.com/xjasonlyu/tun2socks/pkg/log" + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/common/pool" + "github.com/xjasonlyu/tun2socks/log" + "github.com/xjasonlyu/tun2socks/proxy" ) const ( - tcpWaitTimeout = 5 * time.Second - relayBufferSize = pool.RelayBufferSize + tcpWaitTimeout = 5 * time.Second ) func handleTCP(localConn adapter.TCPConn) { @@ -28,12 +26,6 @@ func handleTCP(localConn adapter.TCPConn) { return } - err := resolveMetadata(metadata) - if err != nil { - log.Warnf("[Metadata] resolve metadata error: %v", err) - return - } - targetConn, err := proxy.Dial(metadata) if err != nil { log.Warnf("[TCP] dial %s error: %v", metadata.DestinationAddress(), err) @@ -43,23 +35,23 @@ func handleTCP(localConn adapter.TCPConn) { if dialerAddr, ok := targetConn.LocalAddr().(*net.TCPAddr); ok { metadata.MidIP = dialerAddr.IP metadata.MidPort = uint16(dialerAddr.Port) - } else { + } else { /* fallback */ ip, p, _ := net.SplitHostPort(targetConn.LocalAddr().String()) port, _ := strconv.ParseUint(p, 10, 16) metadata.MidIP = net.ParseIP(ip) metadata.MidPort = uint16(port) } - targetConn = manager.NewTCPTracker(targetConn, metadata) + targetConn = newTCPTracker(targetConn, metadata) defer targetConn.Close() - log.Infof("[TCP] %s <--> %s", metadata.SourceAddress(), metadata.DestinationAddress()) + log.Infof("[TCP] %s <-> %s", metadata.SourceAddress(), metadata.DestinationAddress()) relay(localConn, targetConn) /* relay connections */ } // relay copies between left and right bidirectionally. func relay(left, right net.Conn) { - var wg sync.WaitGroup + wg := sync.WaitGroup{} wg.Add(2) go func() { @@ -78,7 +70,7 @@ func relay(left, right net.Conn) { } func copyBuffer(dst io.Writer, src io.Reader) error { - buf := pool.Get(relayBufferSize) + buf := pool.Get(pool.RelayBufferSize) defer pool.Put(buf) _, err := io.CopyBuffer(dst, src, buf) diff --git a/internal/tunnel/tunnel.go b/tunnel/tunnel.go old mode 100644 new mode 100755 similarity index 60% rename from internal/tunnel/tunnel.go rename to tunnel/tunnel.go index 69ae52d..0e4a5f4 --- a/internal/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -3,8 +3,8 @@ package tunnel import ( "runtime" - "github.com/xjasonlyu/tun2socks/internal/adapter" - "github.com/xjasonlyu/tun2socks/pkg/log" + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/log" ) const ( @@ -15,17 +15,12 @@ const ( ) var ( - numUDPWorkers = max(runtime.NumCPU(), 4 /* at least 4 workers */) - tcpQueue = make(chan adapter.TCPConn) /* unbuffered */ - udpMultiQueue = make([]chan adapter.UDPPacket, 0, numUDPWorkers) + udpQueue = make(chan adapter.UDPPacket, maxUDPQueueSize) + numUDPWorkers = max(runtime.NumCPU(), 4 /* at least 4 workers */) ) func init() { - for i := 0; i < numUDPWorkers; i++ { - udpMultiQueue = append(udpMultiQueue, make(chan adapter.UDPPacket, maxUDPQueueSize)) - } - go process() } @@ -36,23 +31,23 @@ func Add(conn adapter.TCPConn) { // AddPacket adds udpPacket to udpQueue. func AddPacket(packet adapter.UDPPacket) { - m := packet.Metadata() - // In order to keep each packet sent in order, we - // calculate which queue each packet should be sent - // by src/dst info, and make sure the rest of them - // would only be sent to the same queue. - i := int(m.SrcPort+m.DstPort) % numUDPWorkers - select { - case udpMultiQueue[i] <- packet: + case udpQueue <- packet: default: log.Warnf("queue is currently full, packet will be dropped") packet.Drop() } } +func max(a, b int) int { + if a > b { + return a + } + return b +} + func process() { - for _, udpQueue := range udpMultiQueue { + for i := 0; i < numUDPWorkers; i++ { queue := udpQueue go func() { for packet := range queue { diff --git a/internal/tunnel/udp.go b/tunnel/udp.go old mode 100644 new mode 100755 similarity index 71% rename from internal/tunnel/udp.go rename to tunnel/udp.go index 484246f..875ebf7 --- a/internal/tunnel/udp.go +++ b/tunnel/udp.go @@ -7,18 +7,15 @@ import ( "strconv" "time" - "github.com/xjasonlyu/clash/common/pool" - "github.com/xjasonlyu/clash/component/resolver" - "github.com/xjasonlyu/tun2socks/internal/adapter" - "github.com/xjasonlyu/tun2socks/internal/manager" - "github.com/xjasonlyu/tun2socks/internal/proxy" - "github.com/xjasonlyu/tun2socks/pkg/log" - "github.com/xjasonlyu/tun2socks/pkg/nat" + "github.com/xjasonlyu/tun2socks/common/adapter" + "github.com/xjasonlyu/tun2socks/common/pool" + "github.com/xjasonlyu/tun2socks/component/nat" + "github.com/xjasonlyu/tun2socks/log" + "github.com/xjasonlyu/tun2socks/proxy" ) const ( - udpTimeout = 30 * time.Second - udpBufferSize = (1 << 16) - 1 // largest possible UDP datagram + udpSessionTimeout = 30 * time.Second ) var ( @@ -34,18 +31,9 @@ func handleUDP(packet adapter.UDPPacket) { return } - // make a fAddr if request ip is fake ip. - var fAddr net.Addr - if resolver.IsExistFakeIP(metadata.DstIP) { - fAddr = metadata.UDPAddr() + generateNATKey := func(m *adapter.Metadata) string { + return m.SourceAddress() /* Full Cone NAT Key */ } - - err := resolveMetadata(metadata) - if err != nil { - log.Warnf("[Metadata] resolve metadata error: %v", err) - return - } - key := generateNATKey(metadata) handle := func(drop bool) bool { @@ -86,21 +74,21 @@ func handleUDP(packet adapter.UDPPacket) { if dialerAddr, ok := pc.LocalAddr().(*net.UDPAddr); ok { metadata.MidIP = dialerAddr.IP metadata.MidPort = uint16(dialerAddr.Port) - } else { + } else { /* fallback */ ip, p, _ := net.SplitHostPort(pc.LocalAddr().String()) port, _ := strconv.ParseUint(p, 10, 16) metadata.MidIP = net.ParseIP(ip) metadata.MidPort = uint16(port) } - pc = manager.NewUDPTracker(pc, metadata) + pc = newUDPTracker(pc, metadata) go func() { defer pc.Close() defer packet.Drop() defer natTable.Delete(key) - handleUDPToLocal(packet, pc, fAddr, udpTimeout) + handleUDPToLocal(packet, pc, udpSessionTimeout) }() natTable.Set(key, pc) @@ -122,8 +110,8 @@ func handleUDPToRemote(packet adapter.UDPPacket, pc net.PacketConn, remote net.A log.Infof("[UDP] %s --> %s", packet.RemoteAddr(), remote) } -func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, fAddr net.Addr, timeout time.Duration) { - buf := pool.Get(udpBufferSize) +func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, timeout time.Duration) { + buf := pool.Get(pool.MaxSegmentSize) defer pool.Put(buf) for /* just loop */ { @@ -131,15 +119,11 @@ func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, fAddr net.Add n, from, err := pc.ReadFrom(buf) if err != nil { if !errors.Is(err, os.ErrDeadlineExceeded) /* ignore i/o timeout */ { - log.Warnf("[UDP] ReadFrom error: %v", err) + log.Warnf("[UDP] read error: %v", err) } return } - if fAddr != nil { - from = fAddr - } - if _, err := packet.WriteBack(buf[:n], from); err != nil { log.Warnf("[UDP] write back from %s error: %v", from, err) return