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
-
-
## 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