mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2025-10-12 20:20:24 +08:00
Refactor
This commit is contained in:
@@ -1,9 +1,3 @@
|
|||||||
.github/
|
.github
|
||||||
.github/**
|
|
||||||
assets/
|
|
||||||
assets/**
|
|
||||||
.dockerignore
|
|
||||||
*.yml
|
|
||||||
*.md
|
|
||||||
.gitignore
|
.gitignore
|
||||||
Dockerfile*
|
.dockerignore
|
||||||
|
18
.github/workflows/docker.yml
vendored
18
.github/workflows/docker.yml
vendored
@@ -3,13 +3,11 @@ name: Publish Docker Image
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- dev
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.github/**'
|
- 'docs/**'
|
||||||
- 'assets/**'
|
|
||||||
- '.gitignore'
|
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -40,8 +38,15 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
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)
|
- name: Build and Push (dev)
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -49,6 +54,7 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: |
|
tags: |
|
||||||
xjasonlyu/tun2socks:dev
|
xjasonlyu/tun2socks:dev
|
||||||
|
ghcr.io/xjasonlyu/tun2socks:dev
|
||||||
|
|
||||||
- name: Get Version
|
- name: Get Version
|
||||||
id: shell
|
id: shell
|
||||||
@@ -65,3 +71,5 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
xjasonlyu/tun2socks:latest
|
xjasonlyu/tun2socks:latest
|
||||||
xjasonlyu/tun2socks:${{ steps.shell.outputs.version }}
|
xjasonlyu/tun2socks:${{ steps.shell.outputs.version }}
|
||||||
|
ghcr.io/xjasonlyu/tun2socks:latest
|
||||||
|
ghcr.io/xjasonlyu/tun2socks:${{ steps.shell.outputs.version }}
|
||||||
|
12
.github/workflows/go.yml
vendored
12
.github/workflows/go.yml
vendored
@@ -1,19 +1,13 @@
|
|||||||
name: Go
|
name: Go
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
tags:
|
- dev
|
||||||
- '*'
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/**'
|
|
||||||
- 'assets/**'
|
|
||||||
- '.gitignore'
|
|
||||||
- 'README.md'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
16
.github/workflows/stale.yml
vendored
Normal file
16
.github/workflows/stale.yml
vendored
Normal file
@@ -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
|
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,6 +1,14 @@
|
|||||||
|
# Binaries
|
||||||
.idea/
|
*.exe
|
||||||
.vscode/
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
13
Dockerfile
13
Dockerfile
@@ -9,13 +9,14 @@ RUN apk add --no-cache make git \
|
|||||||
&& mv ./bin/tun2socks-docker /tun2socks
|
&& mv ./bin/tun2socks-docker /tun2socks
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/xjasonlyu/tun2socks"
|
||||||
COPY ./scripts/entrypoint.sh /entrypoint.sh
|
|
||||||
COPY --from=builder /tun2socks /usr/bin/tun2socks
|
|
||||||
|
|
||||||
RUN apk add --update --no-cache iptables iproute2 \
|
RUN apk add --update --no-cache iptables iproute2 \
|
||||||
&& chmod +x /entrypoint.sh
|
&& chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
COPY docker/entrypoint.sh /entrypoint.sh
|
||||||
|
COPY --from=builder /tun2socks /usr/bin/tun2socks
|
||||||
|
|
||||||
ENV TUN tun0
|
ENV TUN tun0
|
||||||
ENV ETH eth0
|
ENV ETH eth0
|
||||||
ENV TUN_ADDR=
|
ENV TUN_ADDR=
|
||||||
@@ -23,10 +24,8 @@ ENV TUN_MASK=
|
|||||||
ENV LOGLEVEL=
|
ENV LOGLEVEL=
|
||||||
ENV EXCLUDED=
|
ENV EXCLUDED=
|
||||||
ENV EXTRACMD=
|
ENV EXTRACMD=
|
||||||
|
|
||||||
ENV API=
|
|
||||||
ENV DNS=
|
|
||||||
ENV HOSTS=
|
|
||||||
ENV PROXY=
|
ENV PROXY=
|
||||||
|
ENV STATS=
|
||||||
|
ENV SECRET=
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
@@ -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"]
|
|
48
Makefile
48
Makefile
@@ -1,4 +1,4 @@
|
|||||||
DIR = "bin"
|
BINDIR = "bin"
|
||||||
NAME = "tun2socks"
|
NAME = "tun2socks"
|
||||||
|
|
||||||
TAGS = ""
|
TAGS = ""
|
||||||
@@ -8,9 +8,9 @@ VERSION = $(shell git describe --tags || echo "unknown version")
|
|||||||
BUILD_TIME = $(shell date -u '+%FT%TZ')
|
BUILD_TIME = $(shell date -u '+%FT%TZ')
|
||||||
|
|
||||||
LDFLAGS += -w -s -buildid=
|
LDFLAGS += -w -s -buildid=
|
||||||
LDFLAGS += -X "main.Version=$(VERSION)"
|
LDFLAGS += -X "github.com/xjasonlyu/tun2socks/constant.Version=$(VERSION)"
|
||||||
LDFLAGS += -X "main.BuildTime=$(BUILD_TIME)" # RFC3339
|
LDFLAGS += -X "github.com/xjasonlyu/tun2socks/constant.BuildTime=$(BUILD_TIME)" # RFC3339
|
||||||
GO_BUILD = CGO_ENABLED=0 go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)' -trimpath
|
GO_BUILD = GO111MODULE=on CGO_ENABLED=0 go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)' -trimpath
|
||||||
|
|
||||||
PLATFORM_LIST = \
|
PLATFORM_LIST = \
|
||||||
darwin-amd64 \
|
darwin-amd64 \
|
||||||
@@ -21,40 +21,52 @@ PLATFORM_LIST = \
|
|||||||
openbsd-amd64 \
|
openbsd-amd64 \
|
||||||
openbsd-arm64 \
|
openbsd-arm64 \
|
||||||
|
|
||||||
all: linux-amd64 darwin-amd64
|
WINDOWS_ARCH_LIST = \
|
||||||
|
windows-amd64 \
|
||||||
|
|
||||||
|
all: linux-amd64 darwin-amd64 windows-amd64
|
||||||
|
|
||||||
|
.PHONY: docker
|
||||||
docker:
|
docker:
|
||||||
$(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
$(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
darwin-amd64:
|
darwin-amd64:
|
||||||
GOARCH=amd64 GOOS=darwin $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=amd64 GOOS=darwin $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
freebsd-amd64:
|
freebsd-amd64:
|
||||||
GOARCH=amd64 GOOS=freebsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=amd64 GOOS=freebsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
freebsd-arm64:
|
freebsd-arm64:
|
||||||
GOARCH=arm64 GOOS=freebsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=arm64 GOOS=freebsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
linux-amd64:
|
linux-amd64:
|
||||||
GOARCH=amd64 GOOS=linux $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=amd64 GOOS=linux $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
linux-arm64:
|
linux-arm64:
|
||||||
GOARCH=arm64 GOOS=linux $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=arm64 GOOS=linux $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
openbsd-amd64:
|
openbsd-amd64:
|
||||||
GOARCH=amd64 GOOS=openbsd $(GO_BUILD) -o $(DIR)/$(NAME)-$@
|
GOARCH=amd64 GOOS=openbsd $(GO_BUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
openbsd-arm64:
|
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_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:
|
clean:
|
||||||
rm $(DIR)/*
|
rm $(BINDIR)/*
|
||||||
|
29
README.md
29
README.md
@@ -48,8 +48,6 @@
|
|||||||
|
|
||||||
> iPerf3 tested on Debian 10 with i5-10500, 8G RAM
|
> iPerf3 tested on Debian 10 with i5-10500, 8G RAM
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## How to Build
|
## How to Build
|
||||||
|
|
||||||
### build from source code
|
### build from source code
|
||||||
@@ -250,22 +248,15 @@ PROXY=socks5://server:port LOGLEVEL=INFO sh ./scripts/entrypoint.sh
|
|||||||
<summary><b>Help Text</b></summary>
|
<summary><b>Help Text</b></summary>
|
||||||
|
|
||||||
```text
|
```text
|
||||||
NAME:
|
Usage of tun2socks:
|
||||||
tun2socks - A tun2socks powered by gVisor TCP/IP stack.
|
-d, --device string Use this device [driver://]name
|
||||||
|
-i, --interface string Use network INTERFACE (Darwin/Linux only)
|
||||||
USAGE:
|
-l, --loglevel string Log level [debug|info|warn|error|silent] (default "info")
|
||||||
tun2socks [global options] [arguments...]
|
-m, --mtu int Maximum transmission unit
|
||||||
|
-p, --proxy string Use this proxy [protocol://]host[:port]
|
||||||
GLOBAL OPTIONS:
|
--secret string HTTP statistic server auth secret
|
||||||
--api value URL of external API to listen
|
--stats string HTTP statistic server listen address
|
||||||
--device value, -d value URL of device to open
|
-v, --version Show version information and quit
|
||||||
--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)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
@@ -295,6 +286,6 @@ If you are sensitive to memory, please go back to [v1](https://github.com/xjason
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Windows support
|
- [x] Windows support
|
||||||
- [x] FreeBSD support
|
- [x] FreeBSD support
|
||||||
- [x] OpenBSD support
|
- [x] OpenBSD support
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 242 KiB |
7
internal/adapter/adapter.go → common/adapter/adapter.go
Normal file → Executable file
7
internal/adapter/adapter.go → common/adapter/adapter.go
Normal file → Executable file
@@ -1,6 +1,8 @@
|
|||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
type TCPConn interface {
|
type TCPConn interface {
|
||||||
net.Conn
|
net.Conn
|
||||||
@@ -25,7 +27,6 @@ type UDPPacket interface {
|
|||||||
|
|
||||||
// WriteBack writes the payload with source IP/Port equals addr
|
// WriteBack writes the payload with source IP/Port equals addr
|
||||||
// - variable source IP/Port is important to STUN
|
// - 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,
|
// - 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.
|
|
||||||
WriteBack([]byte, net.Addr) (int, error)
|
WriteBack([]byte, net.Addr) (int, error)
|
||||||
}
|
}
|
55
internal/adapter/metadata.go → common/adapter/metadata.go
Normal file → Executable file
55
internal/adapter/metadata.go → common/adapter/metadata.go
Normal file → Executable file
@@ -2,11 +2,12 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/component/socks5"
|
"github.com/xjasonlyu/tun2socks/component/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -14,17 +15,21 @@ const (
|
|||||||
UDP
|
UDP
|
||||||
)
|
)
|
||||||
|
|
||||||
type Network int
|
type Network uint8
|
||||||
|
|
||||||
func (n Network) String() string {
|
func (n Network) String() string {
|
||||||
if n == TCP {
|
switch n {
|
||||||
|
case TCP:
|
||||||
return "tcp"
|
return "tcp"
|
||||||
|
case UDP:
|
||||||
|
return "udp"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("network(%d)", n)
|
||||||
}
|
}
|
||||||
return "udp"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n Network) MarshalJSON() ([]byte, error) {
|
func (n Network) MarshalText() ([]byte, error) {
|
||||||
return json.Marshal(n.String())
|
return []byte(n.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata implements the net.Addr interface.
|
// Metadata implements the net.Addr interface.
|
||||||
@@ -36,16 +41,10 @@ type Metadata struct {
|
|||||||
SrcPort uint16 `json:"sourcePort"`
|
SrcPort uint16 `json:"sourcePort"`
|
||||||
MidPort uint16 `json:"dialerPort"`
|
MidPort uint16 `json:"dialerPort"`
|
||||||
DstPort uint16 `json:"destinationPort"`
|
DstPort uint16 `json:"destinationPort"`
|
||||||
Host string `json:"host"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) DestinationAddress() string {
|
func (m *Metadata) DestinationAddress() string {
|
||||||
if m.Host != "" {
|
return net.JoinHostPort(m.DstIP.String(), strconv.FormatUint(uint64(m.DstPort), 10))
|
||||||
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 ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) SourceAddress() string {
|
func (m *Metadata) SourceAddress() string {
|
||||||
@@ -63,22 +62,18 @@ func (m *Metadata) UDPAddr() *net.UDPAddr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) SerializesSocksAddr() socks5.Addr {
|
func (m *Metadata) SerializesSocksAddr() socks5.Addr {
|
||||||
var buf [][]byte
|
var (
|
||||||
port := []byte{uint8(m.DstPort >> 8), uint8(m.DstPort & 0xff)}
|
buf [][]byte
|
||||||
switch {
|
port [2]byte
|
||||||
case m.Host != "": /* Domain */
|
)
|
||||||
aType := socks5.AtypDomainName
|
binary.BigEndian.PutUint16(port[:], m.DstPort)
|
||||||
length := uint8(len(m.Host))
|
|
||||||
host := []byte(m.Host)
|
if m.DstIP.To4() != nil /* IPv4 */ {
|
||||||
buf = [][]byte{{uint8(aType), length}, host, port}
|
|
||||||
case m.DstIP.To4() != nil: /* IPv4 */
|
|
||||||
aType := socks5.AtypIPv4
|
aType := socks5.AtypIPv4
|
||||||
host := m.DstIP.To4()
|
buf = [][]byte{{aType}, m.DstIP.To4(), port[:]}
|
||||||
buf = [][]byte{{uint8(aType)}, host, port}
|
} else /* IPv6 */ {
|
||||||
default: /* IPv6 */
|
|
||||||
aType := socks5.AtypIPv6
|
aType := socks5.AtypIPv6
|
||||||
host := m.DstIP.To16()
|
buf = [][]byte{{aType}, m.DstIP.To16(), port[:]}
|
||||||
buf = [][]byte{{uint8(aType)}, host, port}
|
|
||||||
}
|
}
|
||||||
return bytes.Join(buf, nil)
|
return bytes.Join(buf, nil)
|
||||||
}
|
}
|
||||||
@@ -87,10 +82,12 @@ func (m *Metadata) Network() string {
|
|||||||
return m.Net.String()
|
return m.Net.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns destination address of this metadata.
|
||||||
|
// Also for implementing net.Addr interface.
|
||||||
func (m *Metadata) String() string {
|
func (m *Metadata) String() string {
|
||||||
return m.DestinationAddress()
|
return m.DestinationAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) Valid() bool {
|
func (m *Metadata) Valid() bool {
|
||||||
return m.Host != "" || m.DstIP != nil
|
return m.SrcIP != nil && m.DstIP != nil
|
||||||
}
|
}
|
3
common/observable/iterable.go
Executable file
3
common/observable/iterable.go
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
package observable
|
||||||
|
|
||||||
|
type Iterable <-chan interface{}
|
67
common/observable/observable.go
Executable file
67
common/observable/observable.go
Executable file
@@ -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
|
||||||
|
}
|
148
common/observable/observable_test.go
Executable file
148
common/observable/observable_test.go
Executable file
@@ -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()
|
||||||
|
}
|
33
common/observable/subscriber.go
Executable file
33
common/observable/subscriber.go
Executable file
@@ -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
|
||||||
|
}
|
62
common/pool/alloc.go
Executable file
62
common/pool/alloc.go
Executable file
@@ -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<<uint32(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return alloc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a []byte from pool with most appropriate cap.
|
||||||
|
func (alloc *Allocator) Get(size int) []byte {
|
||||||
|
if size <= 0 || size > 65536 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b := msb(size)
|
||||||
|
if size == 1<<b {
|
||||||
|
return alloc.buffers[b].Get().([]byte)[:size]
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc.buffers[b+1].Get().([]byte)[:size]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put returns a []byte to pool for future use,
|
||||||
|
// which the cap must be exactly 2^n.
|
||||||
|
func (alloc *Allocator) Put(buf []byte) error {
|
||||||
|
b := msb(cap(buf))
|
||||||
|
if cap(buf) == 0 || cap(buf) > 65536 || cap(buf) != 1<<b {
|
||||||
|
return errors.New("allocator Put() incorrect buffer size")
|
||||||
|
}
|
||||||
|
|
||||||
|
//lint:ignore SA6002 ignore temporarily
|
||||||
|
alloc.buffers[b].Put(buf)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// msb returns the pos of most significant bit.
|
||||||
|
func msb(size int) uint16 {
|
||||||
|
return uint16(bits.Len32(uint32(size)) - 1)
|
||||||
|
}
|
48
common/pool/alloc_test.go
Executable file
48
common/pool/alloc_test.go
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
package pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAllocGet(t *testing.T) {
|
||||||
|
alloc := NewAllocator()
|
||||||
|
assert.Nil(t, alloc.Get(0))
|
||||||
|
assert.Equal(t, 1, len(alloc.Get(1)))
|
||||||
|
assert.Equal(t, 2, len(alloc.Get(2)))
|
||||||
|
assert.Equal(t, 3, len(alloc.Get(3)))
|
||||||
|
assert.Equal(t, 4, cap(alloc.Get(3)))
|
||||||
|
assert.Equal(t, 4, cap(alloc.Get(4)))
|
||||||
|
assert.Equal(t, 1023, len(alloc.Get(1023)))
|
||||||
|
assert.Equal(t, 1024, cap(alloc.Get(1023)))
|
||||||
|
assert.Equal(t, 1024, len(alloc.Get(1024)))
|
||||||
|
assert.Equal(t, 65536, len(alloc.Get(65536)))
|
||||||
|
assert.Nil(t, alloc.Get(65537))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocPut(t *testing.T) {
|
||||||
|
alloc := NewAllocator()
|
||||||
|
assert.NotNil(t, alloc.Put(nil), "put nil misbehavior")
|
||||||
|
assert.NotNil(t, alloc.Put(make([]byte, 3)), "put elem:3 []bytes misbehavior")
|
||||||
|
assert.Nil(t, alloc.Put(make([]byte, 4)), "put elem:4 []bytes misbehavior")
|
||||||
|
assert.Nil(t, alloc.Put(make([]byte, 1023, 1024)), "put elem:1024 []bytes misbehavior")
|
||||||
|
assert.Nil(t, alloc.Put(make([]byte, 65536)), "put elem:65536 []bytes misbehavior")
|
||||||
|
assert.NotNil(t, alloc.Put(make([]byte, 65537)), "put elem:65537 []bytes misbehavior")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocPutThenGet(t *testing.T) {
|
||||||
|
alloc := NewAllocator()
|
||||||
|
data := alloc.Get(4)
|
||||||
|
_ = alloc.Put(data)
|
||||||
|
newData := alloc.Get(4)
|
||||||
|
|
||||||
|
assert.Equal(t, cap(data), cap(newData), "different cap while alloc.Get()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMSB(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
msb(rand.Int())
|
||||||
|
}
|
||||||
|
}
|
24
common/pool/pool.go
Executable file
24
common/pool/pool.go
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
// Package pool provides a pool of []byte.
|
||||||
|
package pool
|
||||||
|
|
||||||
|
// Ref: github.com/Dreamacro/clash/common/pool
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxSegmentSize is the largest possible UDP datagram size.
|
||||||
|
MaxSegmentSize = (1 << 16) - 1
|
||||||
|
|
||||||
|
// io.Copy default buffer size is 32 KiB, but the maximum packet
|
||||||
|
// size of vmess/shadowsocks is about 16 KiB, so define a buffer
|
||||||
|
// of 20 KiB to reduce the memory of each TCP relay.
|
||||||
|
RelayBufferSize = 20 << 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get gets a []byte from default allocator with most appropriate cap.
|
||||||
|
func Get(size int) []byte {
|
||||||
|
return _allocator.Get(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put returns a []byte to default allocator for future use.
|
||||||
|
func Put(buf []byte) error {
|
||||||
|
return _allocator.Put(buf)
|
||||||
|
}
|
15
component/dialer/bind.go
Executable file
15
component/dialer/bind.go
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
var _boundInterface *net.Interface
|
||||||
|
|
||||||
|
// BindToInterface binds dialer to specific interface.
|
||||||
|
func BindToInterface(name string) error {
|
||||||
|
i, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_boundInterface = i
|
||||||
|
return nil
|
||||||
|
}
|
24
component/dialer/bind_darwin.go
Executable file
24
component/dialer/bind_darwin.go
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindToInterface(network, address string, c syscall.RawConn) error {
|
||||||
|
ipStr, _, _ := net.SplitHostPort(address)
|
||||||
|
if ip := net.ParseIP(ipStr); ip != nil && !ip.IsGlobalUnicast() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
switch network {
|
||||||
|
case "tcp4", "udp4":
|
||||||
|
unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, _boundInterface.Index)
|
||||||
|
case "tcp6", "udp6":
|
||||||
|
unix.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, _boundInterface.Index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
19
component/dialer/bind_linux.go
Executable file
19
component/dialer/bind_linux.go
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindToInterface(network, address string, c syscall.RawConn) error {
|
||||||
|
ipStr, _, _ := net.SplitHostPort(address)
|
||||||
|
if ip := net.ParseIP(ipStr); ip != nil && !ip.IsGlobalUnicast() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
unix.BindToDevice(int(fd), _boundInterface.Name)
|
||||||
|
})
|
||||||
|
}
|
12
component/dialer/bind_others.go
Executable file
12
component/dialer/bind_others.go
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
// +build !linux,!darwin
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindToInterface(network, address string, c syscall.RawConn) error {
|
||||||
|
return errors.New("unsupported platform")
|
||||||
|
}
|
30
component/dialer/dialer.go
Executable file
30
component/dialer/dialer.go
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dial(network, address string) (net.Conn, error) {
|
||||||
|
return DialContext(context.Background(), network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
d := &net.Dialer{}
|
||||||
|
|
||||||
|
if _boundInterface != nil {
|
||||||
|
d.Control = bindToInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenPacket(network, address string) (net.PacketConn, error) {
|
||||||
|
lc := &net.ListenConfig{}
|
||||||
|
|
||||||
|
if _boundInterface != nil {
|
||||||
|
lc.Control = bindToInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
return lc.ListenPacket(context.Background(), network, address)
|
||||||
|
}
|
8
component/dialer/resolver.go
Executable file
8
component/dialer/resolver.go
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
net.DefaultResolver.PreferGo = true
|
||||||
|
net.DefaultResolver.Dial = DialContext
|
||||||
|
}
|
30
internal/manager/manager.go → component/manager/manager.go
Normal file → Executable file
30
internal/manager/manager.go → component/manager/manager.go
Normal file → Executable file
@@ -1,3 +1,4 @@
|
|||||||
|
// Package manager provides statistic management of connections.
|
||||||
package manager
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,21 +8,6 @@ import (
|
|||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultManager *Manager
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
DefaultManager = &Manager{
|
|
||||||
uploadTemp: atomic.NewInt64(0),
|
|
||||||
downloadTemp: atomic.NewInt64(0),
|
|
||||||
uploadBlip: atomic.NewInt64(0),
|
|
||||||
downloadBlip: atomic.NewInt64(0),
|
|
||||||
uploadTotal: atomic.NewInt64(0),
|
|
||||||
downloadTotal: atomic.NewInt64(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
go DefaultManager.handle()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
connections sync.Map
|
connections sync.Map
|
||||||
uploadTemp *atomic.Int64
|
uploadTemp *atomic.Int64
|
||||||
@@ -32,6 +18,20 @@ type Manager struct {
|
|||||||
downloadTotal *atomic.Int64
|
downloadTotal *atomic.Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New() *Manager {
|
||||||
|
manager := &Manager{
|
||||||
|
uploadTemp: atomic.NewInt64(0),
|
||||||
|
downloadTemp: atomic.NewInt64(0),
|
||||||
|
uploadBlip: atomic.NewInt64(0),
|
||||||
|
downloadBlip: atomic.NewInt64(0),
|
||||||
|
uploadTotal: atomic.NewInt64(0),
|
||||||
|
downloadTotal: atomic.NewInt64(0),
|
||||||
|
}
|
||||||
|
go manager.handle()
|
||||||
|
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) Join(c tracker) {
|
func (m *Manager) Join(c tracker) {
|
||||||
m.connections.Store(c.ID(), c)
|
m.connections.Store(c.ID(), c)
|
||||||
}
|
}
|
20
internal/manager/tracker.go → component/manager/tracker.go
Normal file → Executable file
20
internal/manager/tracker.go → component/manager/tracker.go
Normal file → Executable file
@@ -4,10 +4,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
|
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type tracker interface {
|
type tracker interface {
|
||||||
@@ -30,12 +30,12 @@ type tcpTracker struct {
|
|||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPTracker(conn net.Conn, metadata *adapter.Metadata) *tcpTracker {
|
func NewTCPTracker(conn net.Conn, metadata *adapter.Metadata, manager *Manager) *tcpTracker {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV4()
|
||||||
|
|
||||||
t := &tcpTracker{
|
tt := &tcpTracker{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
manager: DefaultManager,
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
UUID: id,
|
UUID: id,
|
||||||
Start: time.Now(),
|
Start: time.Now(),
|
||||||
@@ -45,8 +45,8 @@ func NewTCPTracker(conn net.Conn, metadata *adapter.Metadata) *tcpTracker {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultManager.Join(t)
|
manager.Join(tt)
|
||||||
return t
|
return tt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) ID() string {
|
func (tt *tcpTracker) ID() string {
|
||||||
@@ -81,12 +81,12 @@ type udpTracker struct {
|
|||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPTracker(conn net.PacketConn, metadata *adapter.Metadata) *udpTracker {
|
func NewUDPTracker(conn net.PacketConn, metadata *adapter.Metadata, manager *Manager) *udpTracker {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV4()
|
||||||
|
|
||||||
ut := &udpTracker{
|
ut := &udpTracker{
|
||||||
PacketConn: conn,
|
PacketConn: conn,
|
||||||
manager: DefaultManager,
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
UUID: id,
|
UUID: id,
|
||||||
Start: time.Now(),
|
Start: time.Now(),
|
||||||
@@ -96,7 +96,7 @@ func NewUDPTracker(conn net.PacketConn, metadata *adapter.Metadata) *udpTracker
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultManager.Join(ut)
|
manager.Join(ut)
|
||||||
return ut
|
return ut
|
||||||
}
|
}
|
||||||
|
|
8
pkg/nat/doc.go → component/nat/nat.go
Normal file → Executable file
8
pkg/nat/doc.go → component/nat/nat.go
Normal file → Executable file
@@ -1,26 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
Package nat provides simple NAT table implements.
|
Package nat provides simple NAT table implements.
|
||||||
|
|
||||||
Normal (Full Cone) NAT
|
* Normal (Full Cone) NAT
|
||||||
A full cone NAT is one where all requests from the same internal IP address
|
A full cone NAT is one where all requests from the same internal IP address
|
||||||
and port are mapped to the same external IP address and port. Furthermore,
|
and port are mapped to the same external IP address and port. Furthermore,
|
||||||
any external host can send a packet to the internal host, by sending a packet
|
any external host can send a packet to the internal host, by sending a packet
|
||||||
to the mapped external address.
|
to the mapped external address.
|
||||||
|
|
||||||
Restricted Cone NAT
|
* Restricted Cone NAT
|
||||||
A restricted cone NAT is one where all requests from the same internal IP
|
A restricted cone NAT is one where all requests from the same internal IP
|
||||||
address and port are mapped to the same external IP address and port.
|
address and port are mapped to the same external IP address and port.
|
||||||
Unlike a full cone NAT, an external host (with IP address X) can send a
|
Unlike a full cone NAT, an external host (with IP address X) can send a
|
||||||
packet to the internal host only if the internal host had previously sent
|
packet to the internal host only if the internal host had previously sent
|
||||||
a packet to IP address X.
|
a packet to IP address X.
|
||||||
|
|
||||||
Port Restricted Cone NAT
|
* Port Restricted Cone NAT
|
||||||
A port restricted cone NAT is like a restricted cone NAT, but the restriction
|
A port restricted cone NAT is like a restricted cone NAT, but the restriction
|
||||||
includes port numbers. Specifically, an external host can send a packet, with
|
includes port numbers. Specifically, an external host can send a packet, with
|
||||||
source IP address X and source port P, to the internal host only if the internal
|
source IP address X and source port P, to the internal host only if the internal
|
||||||
host had previously sent a packet to IP address X and port P.
|
host had previously sent a packet to IP address X and port P.
|
||||||
|
|
||||||
Symmetric NAT
|
* Symmetric NAT
|
||||||
A symmetric NAT is one where all requests from the same internal IP address
|
A symmetric NAT is one where all requests from the same internal IP address
|
||||||
and port, to a specific destination IP address and port, are mapped to the
|
and port, to a specific destination IP address and port, are mapped to the
|
||||||
same external IP address and port. If the same host sends a packet with the
|
same external IP address and port. If the same host sends a packet with the
|
0
pkg/nat/table.go → component/nat/table.go
Normal file → Executable file
0
pkg/nat/table.go → component/nat/table.go
Normal file → Executable file
507
component/socks5/rfc1928.txt
Normal file
507
component/socks5/rfc1928.txt
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Network Working Group M. Leech
|
||||||
|
Request for Comments: 1928 Bell-Northern Research Ltd
|
||||||
|
Category: Standards Track M. Ganis
|
||||||
|
International Business Machines
|
||||||
|
Y. Lee
|
||||||
|
NEC Systems Laboratory
|
||||||
|
R. Kuris
|
||||||
|
Unify Corporation
|
||||||
|
D. Koblas
|
||||||
|
Independent Consultant
|
||||||
|
L. Jones
|
||||||
|
Hewlett-Packard Company
|
||||||
|
March 1996
|
||||||
|
|
||||||
|
|
||||||
|
SOCKS Protocol Version 5
|
||||||
|
|
||||||
|
Status of this Memo
|
||||||
|
|
||||||
|
This document specifies an Internet standards track protocol for the
|
||||||
|
Internet community, and requests discussion and suggestions for
|
||||||
|
improvements. Please refer to the current edition of the "Internet
|
||||||
|
Official Protocol Standards" (STD 1) for the standardization state
|
||||||
|
and status of this protocol. Distribution of this memo is unlimited.
|
||||||
|
|
||||||
|
Acknowledgments
|
||||||
|
|
||||||
|
This memo describes a protocol that is an evolution of the previous
|
||||||
|
version of the protocol, version 4 [1]. This new protocol stems from
|
||||||
|
active discussions and prototype implementations. The key
|
||||||
|
contributors are: Marcus Leech: Bell-Northern Research, David Koblas:
|
||||||
|
Independent Consultant, Ying-Da Lee: NEC Systems Laboratory, LaMont
|
||||||
|
Jones: Hewlett-Packard Company, Ron Kuris: Unify Corporation, Matt
|
||||||
|
Ganis: International Business Machines.
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
|
||||||
|
The use of network firewalls, systems that effectively isolate an
|
||||||
|
organizations internal network structure from an exterior network,
|
||||||
|
such as the INTERNET is becoming increasingly popular. These
|
||||||
|
firewall systems typically act as application-layer gateways between
|
||||||
|
networks, usually offering controlled TELNET, FTP, and SMTP access.
|
||||||
|
With the emergence of more sophisticated application layer protocols
|
||||||
|
designed to facilitate global information discovery, there exists a
|
||||||
|
need to provide a general framework for these protocols to
|
||||||
|
transparently and securely traverse a firewall.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 1]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
There exists, also, a need for strong authentication of such
|
||||||
|
traversal in as fine-grained a manner as is practical. This
|
||||||
|
requirement stems from the realization that client-server
|
||||||
|
relationships emerge between the networks of various organizations,
|
||||||
|
and that such relationships need to be controlled and often strongly
|
||||||
|
authenticated.
|
||||||
|
|
||||||
|
The protocol described here is designed to provide a framework for
|
||||||
|
client-server applications in both the TCP and UDP domains to
|
||||||
|
conveniently and securely use the services of a network firewall.
|
||||||
|
The protocol is conceptually a "shim-layer" between the application
|
||||||
|
layer and the transport layer, and as such does not provide network-
|
||||||
|
layer gateway services, such as forwarding of ICMP messages.
|
||||||
|
|
||||||
|
2. Existing practice
|
||||||
|
|
||||||
|
There currently exists a protocol, SOCKS Version 4, that provides for
|
||||||
|
unsecured firewall traversal for TCP-based client-server
|
||||||
|
applications, including TELNET, FTP and the popular information-
|
||||||
|
discovery protocols such as HTTP, WAIS and GOPHER.
|
||||||
|
|
||||||
|
This new protocol extends the SOCKS Version 4 model to include UDP,
|
||||||
|
and extends the framework to include provisions for generalized
|
||||||
|
strong authentication schemes, and extends the addressing scheme to
|
||||||
|
encompass domain-name and V6 IP addresses.
|
||||||
|
|
||||||
|
The implementation of the SOCKS protocol typically involves the
|
||||||
|
recompilation or relinking of TCP-based client applications to use
|
||||||
|
the appropriate encapsulation routines in the SOCKS library.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
Unless otherwise noted, the decimal numbers appearing in packet-
|
||||||
|
format diagrams represent the length of the corresponding field, in
|
||||||
|
octets. Where a given octet must take on a specific value, the
|
||||||
|
syntax X'hh' is used to denote the value of the single octet in that
|
||||||
|
field. When the word 'Variable' is used, it indicates that the
|
||||||
|
corresponding field has a variable length defined either by an
|
||||||
|
associated (one or two octet) length field, or by a data type field.
|
||||||
|
|
||||||
|
3. Procedure for TCP-based clients
|
||||||
|
|
||||||
|
When a TCP-based client wishes to establish a connection to an object
|
||||||
|
that is reachable only via a firewall (such determination is left up
|
||||||
|
to the implementation), it must open a TCP connection to the
|
||||||
|
appropriate SOCKS port on the SOCKS server system. The SOCKS service
|
||||||
|
is conventionally located on TCP port 1080. If the connection
|
||||||
|
request succeeds, the client enters a negotiation for the
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 2]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
authentication method to be used, authenticates with the chosen
|
||||||
|
method, then sends a relay request. The SOCKS server evaluates the
|
||||||
|
request, and either establishes the appropriate connection or denies
|
||||||
|
it.
|
||||||
|
|
||||||
|
Unless otherwise noted, the decimal numbers appearing in packet-
|
||||||
|
format diagrams represent the length of the corresponding field, in
|
||||||
|
octets. Where a given octet must take on a specific value, the
|
||||||
|
syntax X'hh' is used to denote the value of the single octet in that
|
||||||
|
field. When the word 'Variable' is used, it indicates that the
|
||||||
|
corresponding field has a variable length defined either by an
|
||||||
|
associated (one or two octet) length field, or by a data type field.
|
||||||
|
|
||||||
|
The client connects to the server, and sends a version
|
||||||
|
identifier/method selection message:
|
||||||
|
|
||||||
|
+----+----------+----------+
|
||||||
|
|VER | NMETHODS | METHODS |
|
||||||
|
+----+----------+----------+
|
||||||
|
| 1 | 1 | 1 to 255 |
|
||||||
|
+----+----------+----------+
|
||||||
|
|
||||||
|
The VER field is set to X'05' for this version of the protocol. The
|
||||||
|
NMETHODS field contains the number of method identifier octets that
|
||||||
|
appear in the METHODS field.
|
||||||
|
|
||||||
|
The server selects from one of the methods given in METHODS, and
|
||||||
|
sends a METHOD selection message:
|
||||||
|
|
||||||
|
+----+--------+
|
||||||
|
|VER | METHOD |
|
||||||
|
+----+--------+
|
||||||
|
| 1 | 1 |
|
||||||
|
+----+--------+
|
||||||
|
|
||||||
|
If the selected METHOD is X'FF', none of the methods listed by the
|
||||||
|
client are acceptable, and the client MUST close the connection.
|
||||||
|
|
||||||
|
The values currently defined for METHOD are:
|
||||||
|
|
||||||
|
o X'00' NO AUTHENTICATION REQUIRED
|
||||||
|
o X'01' GSSAPI
|
||||||
|
o X'02' USERNAME/PASSWORD
|
||||||
|
o X'03' to X'7F' IANA ASSIGNED
|
||||||
|
o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
|
||||||
|
o X'FF' NO ACCEPTABLE METHODS
|
||||||
|
|
||||||
|
The client and server then enter a method-specific sub-negotiation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 3]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
Descriptions of the method-dependent sub-negotiations appear in
|
||||||
|
separate memos.
|
||||||
|
|
||||||
|
Developers of new METHOD support for this protocol should contact
|
||||||
|
IANA for a METHOD number. The ASSIGNED NUMBERS document should be
|
||||||
|
referred to for a current list of METHOD numbers and their
|
||||||
|
corresponding protocols.
|
||||||
|
|
||||||
|
Compliant implementations MUST support GSSAPI and SHOULD support
|
||||||
|
USERNAME/PASSWORD authentication methods.
|
||||||
|
|
||||||
|
4. Requests
|
||||||
|
|
||||||
|
Once the method-dependent subnegotiation has completed, the client
|
||||||
|
sends the request details. If the negotiated method includes
|
||||||
|
encapsulation for purposes of integrity checking and/or
|
||||||
|
confidentiality, these requests MUST be encapsulated in the method-
|
||||||
|
dependent encapsulation.
|
||||||
|
|
||||||
|
The SOCKS request is formed as follows:
|
||||||
|
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
| 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
o VER protocol version: X'05'
|
||||||
|
o CMD
|
||||||
|
o CONNECT X'01'
|
||||||
|
o BIND X'02'
|
||||||
|
o UDP ASSOCIATE X'03'
|
||||||
|
o RSV RESERVED
|
||||||
|
o ATYP address type of following address
|
||||||
|
o IP V4 address: X'01'
|
||||||
|
o DOMAINNAME: X'03'
|
||||||
|
o IP V6 address: X'04'
|
||||||
|
o DST.ADDR desired destination address
|
||||||
|
o DST.PORT desired destination port in network octet
|
||||||
|
order
|
||||||
|
|
||||||
|
The SOCKS server will typically evaluate the request based on source
|
||||||
|
and destination addresses, and return one or more reply messages, as
|
||||||
|
appropriate for the request type.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 4]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
5. Addressing
|
||||||
|
|
||||||
|
In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
|
||||||
|
the type of address contained within the field:
|
||||||
|
|
||||||
|
o X'01'
|
||||||
|
|
||||||
|
the address is a version-4 IP address, with a length of 4 octets
|
||||||
|
|
||||||
|
o X'03'
|
||||||
|
|
||||||
|
the address field contains a fully-qualified domain name. The first
|
||||||
|
octet of the address field contains the number of octets of name that
|
||||||
|
follow, there is no terminating NUL octet.
|
||||||
|
|
||||||
|
o X'04'
|
||||||
|
|
||||||
|
the address is a version-6 IP address, with a length of 16 octets.
|
||||||
|
|
||||||
|
6. Replies
|
||||||
|
|
||||||
|
The SOCKS request information is sent by the client as soon as it has
|
||||||
|
established a connection to the SOCKS server, and completed the
|
||||||
|
authentication negotiations. The server evaluates the request, and
|
||||||
|
returns a reply formed as follows:
|
||||||
|
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
| 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||||
|
+----+-----+-------+------+----------+----------+
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
o VER protocol version: X'05'
|
||||||
|
o REP Reply field:
|
||||||
|
o X'00' succeeded
|
||||||
|
o X'01' general SOCKS server failure
|
||||||
|
o X'02' connection not allowed by ruleset
|
||||||
|
o X'03' Network unreachable
|
||||||
|
o X'04' Host unreachable
|
||||||
|
o X'05' Connection refused
|
||||||
|
o X'06' TTL expired
|
||||||
|
o X'07' Command not supported
|
||||||
|
o X'08' Address type not supported
|
||||||
|
o X'09' to X'FF' unassigned
|
||||||
|
o RSV RESERVED
|
||||||
|
o ATYP address type of following address
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 5]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
o IP V4 address: X'01'
|
||||||
|
o DOMAINNAME: X'03'
|
||||||
|
o IP V6 address: X'04'
|
||||||
|
o BND.ADDR server bound address
|
||||||
|
o BND.PORT server bound port in network octet order
|
||||||
|
|
||||||
|
Fields marked RESERVED (RSV) must be set to X'00'.
|
||||||
|
|
||||||
|
If the chosen method includes encapsulation for purposes of
|
||||||
|
authentication, integrity and/or confidentiality, the replies are
|
||||||
|
encapsulated in the method-dependent encapsulation.
|
||||||
|
|
||||||
|
CONNECT
|
||||||
|
|
||||||
|
In the reply to a CONNECT, BND.PORT contains the port number that the
|
||||||
|
server assigned to connect to the target host, while BND.ADDR
|
||||||
|
contains the associated IP address. The supplied BND.ADDR is often
|
||||||
|
different from the IP address that the client uses to reach the SOCKS
|
||||||
|
server, since such servers are often multi-homed. It is expected
|
||||||
|
that the SOCKS server will use DST.ADDR and DST.PORT, and the
|
||||||
|
client-side source address and port in evaluating the CONNECT
|
||||||
|
request.
|
||||||
|
|
||||||
|
BIND
|
||||||
|
|
||||||
|
The BIND request is used in protocols which require the client to
|
||||||
|
accept connections from the server. FTP is a well-known example,
|
||||||
|
which uses the primary client-to-server connection for commands and
|
||||||
|
status reports, but may use a server-to-client connection for
|
||||||
|
transferring data on demand (e.g. LS, GET, PUT).
|
||||||
|
|
||||||
|
It is expected that the client side of an application protocol will
|
||||||
|
use the BIND request only to establish secondary connections after a
|
||||||
|
primary connection is established using CONNECT. In is expected that
|
||||||
|
a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND
|
||||||
|
request.
|
||||||
|
|
||||||
|
Two replies are sent from the SOCKS server to the client during a
|
||||||
|
BIND operation. The first is sent after the server creates and binds
|
||||||
|
a new socket. The BND.PORT field contains the port number that the
|
||||||
|
SOCKS server assigned to listen for an incoming connection. The
|
||||||
|
BND.ADDR field contains the associated IP address. The client will
|
||||||
|
typically use these pieces of information to notify (via the primary
|
||||||
|
or control connection) the application server of the rendezvous
|
||||||
|
address. The second reply occurs only after the anticipated incoming
|
||||||
|
connection succeeds or fails.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 6]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
In the second reply, the BND.PORT and BND.ADDR fields contain the
|
||||||
|
address and port number of the connecting host.
|
||||||
|
|
||||||
|
UDP ASSOCIATE
|
||||||
|
|
||||||
|
The UDP ASSOCIATE request is used to establish an association within
|
||||||
|
the UDP relay process to handle UDP datagrams. The DST.ADDR and
|
||||||
|
DST.PORT fields contain the address and port that the client expects
|
||||||
|
to use to send UDP datagrams on for the association. The server MAY
|
||||||
|
use this information to limit access to the association. If the
|
||||||
|
client is not in possesion of the information at the time of the UDP
|
||||||
|
ASSOCIATE, the client MUST use a port number and address of all
|
||||||
|
zeros.
|
||||||
|
|
||||||
|
A UDP association terminates when the TCP connection that the UDP
|
||||||
|
ASSOCIATE request arrived on terminates.
|
||||||
|
|
||||||
|
In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR
|
||||||
|
fields indicate the port number/address where the client MUST send
|
||||||
|
UDP request messages to be relayed.
|
||||||
|
|
||||||
|
Reply Processing
|
||||||
|
|
||||||
|
When a reply (REP value other than X'00') indicates a failure, the
|
||||||
|
SOCKS server MUST terminate the TCP connection shortly after sending
|
||||||
|
the reply. This must be no more than 10 seconds after detecting the
|
||||||
|
condition that caused a failure.
|
||||||
|
|
||||||
|
If the reply code (REP value of X'00') indicates a success, and the
|
||||||
|
request was either a BIND or a CONNECT, the client may now start
|
||||||
|
passing data. If the selected authentication method supports
|
||||||
|
encapsulation for the purposes of integrity, authentication and/or
|
||||||
|
confidentiality, the data are encapsulated using the method-dependent
|
||||||
|
encapsulation. Similarly, when data arrives at the SOCKS server for
|
||||||
|
the client, the server MUST encapsulate the data as appropriate for
|
||||||
|
the authentication method in use.
|
||||||
|
|
||||||
|
7. Procedure for UDP-based clients
|
||||||
|
|
||||||
|
A UDP-based client MUST send its datagrams to the UDP relay server at
|
||||||
|
the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE
|
||||||
|
request. If the selected authentication method provides
|
||||||
|
encapsulation for the purposes of authenticity, integrity, and/or
|
||||||
|
confidentiality, the datagram MUST be encapsulated using the
|
||||||
|
appropriate encapsulation. Each UDP datagram carries a UDP request
|
||||||
|
header with it:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 7]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
+----+------+------+----------+----------+----------+
|
||||||
|
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||||||
|
+----+------+------+----------+----------+----------+
|
||||||
|
| 2 | 1 | 1 | Variable | 2 | Variable |
|
||||||
|
+----+------+------+----------+----------+----------+
|
||||||
|
|
||||||
|
The fields in the UDP request header are:
|
||||||
|
|
||||||
|
o RSV Reserved X'0000'
|
||||||
|
o FRAG Current fragment number
|
||||||
|
o ATYP address type of following addresses:
|
||||||
|
o IP V4 address: X'01'
|
||||||
|
o DOMAINNAME: X'03'
|
||||||
|
o IP V6 address: X'04'
|
||||||
|
o DST.ADDR desired destination address
|
||||||
|
o DST.PORT desired destination port
|
||||||
|
o DATA user data
|
||||||
|
|
||||||
|
When a UDP relay server decides to relay a UDP datagram, it does so
|
||||||
|
silently, without any notification to the requesting client.
|
||||||
|
Similarly, it will drop datagrams it cannot or will not relay. When
|
||||||
|
a UDP relay server receives a reply datagram from a remote host, it
|
||||||
|
MUST encapsulate that datagram using the above UDP request header,
|
||||||
|
and any authentication-method-dependent encapsulation.
|
||||||
|
|
||||||
|
The UDP relay server MUST acquire from the SOCKS server the expected
|
||||||
|
IP address of the client that will send datagrams to the BND.PORT
|
||||||
|
given in the reply to UDP ASSOCIATE. It MUST drop any datagrams
|
||||||
|
arriving from any source IP address other than the one recorded for
|
||||||
|
the particular association.
|
||||||
|
|
||||||
|
The FRAG field indicates whether or not this datagram is one of a
|
||||||
|
number of fragments. If implemented, the high-order bit indicates
|
||||||
|
end-of-fragment sequence, while a value of X'00' indicates that this
|
||||||
|
datagram is standalone. Values between 1 and 127 indicate the
|
||||||
|
fragment position within a fragment sequence. Each receiver will
|
||||||
|
have a REASSEMBLY QUEUE and a REASSEMBLY TIMER associated with these
|
||||||
|
fragments. The reassembly queue must be reinitialized and the
|
||||||
|
associated fragments abandoned whenever the REASSEMBLY TIMER expires,
|
||||||
|
or a new datagram arrives carrying a FRAG field whose value is less
|
||||||
|
than the highest FRAG value processed for this fragment sequence.
|
||||||
|
The reassembly timer MUST be no less than 5 seconds. It is
|
||||||
|
recommended that fragmentation be avoided by applications wherever
|
||||||
|
possible.
|
||||||
|
|
||||||
|
Implementation of fragmentation is optional; an implementation that
|
||||||
|
does not support fragmentation MUST drop any datagram whose FRAG
|
||||||
|
field is other than X'00'.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 8]
|
||||||
|
|
||||||
|
RFC 1928 SOCKS Protocol Version 5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
The programming interface for a SOCKS-aware UDP MUST report an
|
||||||
|
available buffer space for UDP datagrams that is smaller than the
|
||||||
|
actual space provided by the operating system:
|
||||||
|
|
||||||
|
o if ATYP is X'01' - 10+method_dependent octets smaller
|
||||||
|
o if ATYP is X'03' - 262+method_dependent octets smaller
|
||||||
|
o if ATYP is X'04' - 20+method_dependent octets smaller
|
||||||
|
|
||||||
|
8. Security Considerations
|
||||||
|
|
||||||
|
This document describes a protocol for the application-layer
|
||||||
|
traversal of IP network firewalls. The security of such traversal is
|
||||||
|
highly dependent on the particular authentication and encapsulation
|
||||||
|
methods provided in a particular implementation, and selected during
|
||||||
|
negotiation between SOCKS client and SOCKS server.
|
||||||
|
|
||||||
|
Careful consideration should be given by the administrator to the
|
||||||
|
selection of authentication methods.
|
||||||
|
|
||||||
|
9. References
|
||||||
|
|
||||||
|
[1] Koblas, D., "SOCKS", Proceedings: 1992 Usenix Security Symposium.
|
||||||
|
|
||||||
|
Author's Address
|
||||||
|
|
||||||
|
Marcus Leech
|
||||||
|
Bell-Northern Research Ltd
|
||||||
|
P.O. Box 3511, Stn. C,
|
||||||
|
Ottawa, ON
|
||||||
|
CANADA K1Y 4H7
|
||||||
|
|
||||||
|
Phone: (613) 763-9145
|
||||||
|
EMail: mleech@bnr.ca
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech, et al Standards Track [Page 9]
|
||||||
|
|
115
component/socks5/rfc1929.txt
Normal file
115
component/socks5/rfc1929.txt
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Network Working Group M. Leech
|
||||||
|
Request for Comments: 1929 Bell-Northern Research Ltd
|
||||||
|
Category: Standards Track March 1996
|
||||||
|
|
||||||
|
|
||||||
|
Username/Password Authentication for SOCKS V5
|
||||||
|
|
||||||
|
Status of this Memo
|
||||||
|
|
||||||
|
This document specifies an Internet standards track protocol for the
|
||||||
|
Internet community, and requests discussion and suggestions for
|
||||||
|
improvements. Please refer to the current edition of the "Internet
|
||||||
|
Official Protocol Standards" (STD 1) for the standardization state
|
||||||
|
and status of this protocol. Distribution of this memo is unlimited.
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
|
||||||
|
The protocol specification for SOCKS Version 5 specifies a
|
||||||
|
generalized framework for the use of arbitrary authentication
|
||||||
|
protocols in the initial socks connection setup. This document
|
||||||
|
describes one of those protocols, as it fits into the SOCKS Version 5
|
||||||
|
authentication "subnegotiation".
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
Unless otherwise noted, the decimal numbers appearing in packet-
|
||||||
|
format diagrams represent the length of the corresponding field, in
|
||||||
|
octets. Where a given octet must take on a specific value, the
|
||||||
|
syntax X'hh' is used to denote the value of the single octet in that
|
||||||
|
field. When the word 'Variable' is used, it indicates that the
|
||||||
|
corresponding field has a variable length defined either by an
|
||||||
|
associated (one or two octet) length field, or by a data type field.
|
||||||
|
|
||||||
|
2. Initial negotiation
|
||||||
|
|
||||||
|
Once the SOCKS V5 server has started, and the client has selected the
|
||||||
|
Username/Password Authentication protocol, the Username/Password
|
||||||
|
subnegotiation begins. This begins with the client producing a
|
||||||
|
Username/Password request:
|
||||||
|
|
||||||
|
+----+------+----------+------+----------+
|
||||||
|
|VER | ULEN | UNAME | PLEN | PASSWD |
|
||||||
|
+----+------+----------+------+----------+
|
||||||
|
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
|
||||||
|
+----+------+----------+------+----------+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech Standards Track [Page 1]
|
||||||
|
|
||||||
|
RFC 1929 Username Authentication for SOCKS V5 March 1996
|
||||||
|
|
||||||
|
|
||||||
|
The VER field contains the current version of the subnegotiation,
|
||||||
|
which is X'01'. The ULEN field contains the length of the UNAME field
|
||||||
|
that follows. The UNAME field contains the username as known to the
|
||||||
|
source operating system. The PLEN field contains the length of the
|
||||||
|
PASSWD field that follows. The PASSWD field contains the password
|
||||||
|
association with the given UNAME.
|
||||||
|
|
||||||
|
The server verifies the supplied UNAME and PASSWD, and sends the
|
||||||
|
following response:
|
||||||
|
|
||||||
|
+----+--------+
|
||||||
|
|VER | STATUS |
|
||||||
|
+----+--------+
|
||||||
|
| 1 | 1 |
|
||||||
|
+----+--------+
|
||||||
|
|
||||||
|
A STATUS field of X'00' indicates success. If the server returns a
|
||||||
|
`failure' (STATUS value other than X'00') status, it MUST close the
|
||||||
|
connection.
|
||||||
|
|
||||||
|
3. Security Considerations
|
||||||
|
|
||||||
|
This document describes a subnegotiation that provides authentication
|
||||||
|
services to the SOCKS protocol. Since the request carries the
|
||||||
|
password in cleartext, this subnegotiation is not recommended for
|
||||||
|
environments where "sniffing" is possible and practical.
|
||||||
|
|
||||||
|
4. Author's Address
|
||||||
|
|
||||||
|
Marcus Leech
|
||||||
|
Bell-Northern Research Ltd
|
||||||
|
P.O. Box 3511, Station C
|
||||||
|
Ottawa, ON
|
||||||
|
CANADA K1Y 4H7
|
||||||
|
|
||||||
|
Phone: +1 613 763 9145
|
||||||
|
EMail: mleech@bnr.ca
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Leech Standards Track [Page 2]
|
||||||
|
|
355
component/socks5/socks5.go
Executable file
355
component/socks5/socks5.go
Executable file
@@ -0,0 +1,355 @@
|
|||||||
|
// Package socks5 provides SOCKS5 client functionalities.
|
||||||
|
package socks5
|
||||||
|
|
||||||
|
// Ref: github.com/Dreamacro/clash/component/socks5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version is the protocol version as defined in RFC 1928 section 4.
|
||||||
|
const Version = 0x05
|
||||||
|
|
||||||
|
// Command is request commands as defined in RFC 1928 section 4.
|
||||||
|
type Command = uint8
|
||||||
|
|
||||||
|
// SOCKS request commands as defined in RFC 1928 section 4.
|
||||||
|
const (
|
||||||
|
CmdConnect Command = 0x01
|
||||||
|
CmdBind Command = 0x02
|
||||||
|
CmdUDPAssociate Command = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
type Atyp = uint8
|
||||||
|
|
||||||
|
// SOCKS address types as defined in RFC 1928 section 5.
|
||||||
|
const (
|
||||||
|
AtypIPv4 Atyp = 0x01
|
||||||
|
AtypDomainName Atyp = 0x03
|
||||||
|
AtypIPv6 Atyp = 0x04
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxAddrLen is the maximum size of SOCKS address in bytes.
|
||||||
|
const MaxAddrLen = 1 + 1 + 255 + 2
|
||||||
|
|
||||||
|
// MaxAuthLen is the maximum size of user/password field in SOCKS auth.
|
||||||
|
const MaxAuthLen = 255
|
||||||
|
|
||||||
|
// Addr represents a SOCKS address as defined in RFC 1928 section 5.
|
||||||
|
type Addr []byte
|
||||||
|
|
||||||
|
func (a Addr) Valid() bool {
|
||||||
|
if len(a) < 1+1+2 /* minimum length */ {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a[0] {
|
||||||
|
case AtypDomainName:
|
||||||
|
if len(a) < 1+1+int(a[1])+2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case AtypIPv4:
|
||||||
|
if len(a) < 1+net.IPv4len+2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case AtypIPv6:
|
||||||
|
if len(a) < 1+net.IPv6len+2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns string of socks5.Addr.
|
||||||
|
func (a Addr) String() string {
|
||||||
|
if !a.Valid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var host, port string
|
||||||
|
switch a[0] {
|
||||||
|
case AtypDomainName:
|
||||||
|
hostLen := int(a[1])
|
||||||
|
host = string(a[2 : 2+hostLen])
|
||||||
|
port = strconv.Itoa(int(binary.BigEndian.Uint16(a[2+hostLen:])))
|
||||||
|
case AtypIPv4:
|
||||||
|
host = net.IP(a[1 : 1+net.IPv4len]).String()
|
||||||
|
port = strconv.Itoa(int(binary.BigEndian.Uint16(a[1+net.IPv4len:])))
|
||||||
|
case AtypIPv6:
|
||||||
|
host = net.IP(a[1 : 1+net.IPv6len]).String()
|
||||||
|
port = strconv.Itoa(int(binary.BigEndian.Uint16(a[1+net.IPv6len:])))
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDPAddr converts a socks5.Addr to *net.UDPAddr.
|
||||||
|
func (a Addr) UDPAddr() *net.UDPAddr {
|
||||||
|
if !a.Valid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip []byte
|
||||||
|
var port int
|
||||||
|
switch a[0] {
|
||||||
|
case AtypDomainName /* unsupported */ :
|
||||||
|
return nil
|
||||||
|
case AtypIPv4:
|
||||||
|
ip = make([]byte, net.IPv4len)
|
||||||
|
copy(ip, a[1:1+net.IPv4len])
|
||||||
|
port = int(binary.BigEndian.Uint16(a[1+net.IPv4len:]))
|
||||||
|
case AtypIPv6:
|
||||||
|
ip = make([]byte, net.IPv6len)
|
||||||
|
copy(ip, a[1:1+net.IPv6len])
|
||||||
|
port = int(binary.BigEndian.Uint16(a[1+net.IPv6len:]))
|
||||||
|
}
|
||||||
|
return &net.UDPAddr{IP: ip, Port: port}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User provides basic socks5 auth functionality.
|
||||||
|
type User struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshake fast-tracks SOCKS initialization to get target address to connect on client side.
|
||||||
|
func ClientHandshake(rw io.ReadWriter, addr Addr, command Command, user *User) (Addr, error) {
|
||||||
|
buf := make([]byte, MaxAddrLen)
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// VER, NMETHODS, METHODS
|
||||||
|
if user != nil {
|
||||||
|
_, err = rw.Write([]byte{Version, 0x01 /* NMETHODS */, 0x02 /* USERNAME/PASSWORD */})
|
||||||
|
} else {
|
||||||
|
_, err = rw.Write([]byte{Version, 0x01 /* NMETHODS */, 0x00 /* NO AUTHENTICATION REQUIRED */})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VER, METHOD
|
||||||
|
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf[0] != Version {
|
||||||
|
return nil, errors.New("socks version mismatched")
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf[1] == 0x02 /* USERNAME/PASSWORD */ {
|
||||||
|
if user == nil {
|
||||||
|
return nil, errors.New("auth required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// password protocol version
|
||||||
|
authMsg := &bytes.Buffer{}
|
||||||
|
authMsg.WriteByte(0x01 /* VER */)
|
||||||
|
authMsg.WriteByte(byte(len(user.Username)) /* ULEN */)
|
||||||
|
authMsg.WriteString(user.Username /* UNAME */)
|
||||||
|
authMsg.WriteByte(byte(len(user.Password)) /* PLEN */)
|
||||||
|
authMsg.WriteString(user.Password /* PASSWD */)
|
||||||
|
|
||||||
|
if len(authMsg.Bytes()) > 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
|
||||||
|
}
|
2
constant/constant.go
Normal file
2
constant/constant.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package constant provides global constants/variables of this project.
|
||||||
|
package constant
|
8
constant/version.go
Executable file
8
constant/version.go
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const Name = "tun2socks"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version = ""
|
||||||
|
BuildTime = ""
|
||||||
|
)
|
17
device/device.go
Executable file
17
device/device.go
Executable file
@@ -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
|
||||||
|
}
|
67
pkg/link/rwc/endpoint.go → device/rwbased/endpoint.go
Normal file → Executable file
67
pkg/link/rwc/endpoint.go → device/rwbased/endpoint.go
Normal file → Executable file
@@ -1,9 +1,10 @@
|
|||||||
package rwc
|
// Package channel provides the implementation of io.ReadWriter
|
||||||
|
// based data-link layer endpoints.
|
||||||
|
package rwbased
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||||
@@ -11,28 +12,34 @@ import (
|
|||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"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 {
|
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
|
mtu uint32
|
||||||
rwc io.ReadWriteCloser
|
|
||||||
|
|
||||||
wg sync.WaitGroup
|
// caps holds the endpoint capabilities.
|
||||||
|
caps stack.LinkEndpointCapabilities
|
||||||
|
|
||||||
dispatcher stack.NetworkDispatcher
|
dispatcher stack.NetworkDispatcher
|
||||||
LinkEPCapabilities stack.LinkEndpointCapabilities
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns stack.LinkEndpoint(.*Endpoint) and error.
|
// New returns stack.LinkEndpoint(.*Endpoint) and error.
|
||||||
func New(rwc io.ReadWriteCloser, mtu uint32) (*Endpoint, error) {
|
func New(rw io.ReadWriter, mtu uint32) (*Endpoint, error) {
|
||||||
switch {
|
if mtu == 0 {
|
||||||
case mtu == 0:
|
|
||||||
return nil, errors.New("MTU size is zero")
|
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{
|
return &Endpoint{
|
||||||
rwc: rwc,
|
rw: rw,
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -51,12 +58,10 @@ func (e *Endpoint) IsAttached() bool {
|
|||||||
|
|
||||||
// dispatchLoop dispatches packets to upper layer.
|
// dispatchLoop dispatches packets to upper layer.
|
||||||
func (e *Endpoint) dispatchLoop() {
|
func (e *Endpoint) dispatchLoop() {
|
||||||
e.wg.Add(1)
|
|
||||||
defer e.wg.Done()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
packet := make([]byte, e.mtu)
|
packet := make([]byte, e.mtu)
|
||||||
n, err := e.rwc.Read(packet)
|
|
||||||
|
n, err := e.rw.Read(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
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()
|
networkHdr := pkt.NetworkHeader().View()
|
||||||
transportHdr := pkt.TransportHeader().View()
|
transportHdr := pkt.TransportHeader().View()
|
||||||
payload := pkt.Data.ToView()
|
payload := pkt.Data.ToView()
|
||||||
@@ -90,20 +94,20 @@ func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) *tcpip.Error {
|
|||||||
[]buffer.View{networkHdr, transportHdr, payload},
|
[]buffer.View{networkHdr, transportHdr, payload},
|
||||||
)
|
)
|
||||||
|
|
||||||
if _, err := e.rwc.Write(buf.ToView()); err != nil {
|
if _, err := e.rw.Write(buf.ToView()); err != nil {
|
||||||
return tcpip.ErrInvalidEndpointState
|
return &tcpip.ErrInvalidEndpointState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePacket writes packets back into io.ReadWriter.
|
// WritePacket writes packet back into io.ReadWriter.
|
||||||
func (e *Endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) *tcpip.Error {
|
func (e *Endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||||
return e.writePacket(pkt)
|
return e.writePacket(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePackets writes packets back into io.ReadWriter.
|
// 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
|
n := 0
|
||||||
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
|
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
|
||||||
if err := e.writePacket(pkt); err != nil {
|
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.
|
// 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{
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
Data: vv,
|
Data: vv,
|
||||||
})
|
})
|
||||||
@@ -129,12 +133,12 @@ func (e *Endpoint) MTU() uint32 {
|
|||||||
|
|
||||||
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
||||||
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||||
return e.LinkEPCapabilities
|
return e.caps
|
||||||
}
|
}
|
||||||
|
|
||||||
// GSOMaxSize returns the maximum GSO packet size.
|
// GSOMaxSize returns the maximum GSO packet size.
|
||||||
func (*Endpoint) GSOMaxSize() uint32 {
|
func (*Endpoint) GSOMaxSize() uint32 {
|
||||||
return 1 << 15
|
return 1 << 15 /* default */
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxHeaderLength returns the maximum size of the link layer header. Given it
|
// 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.
|
// Wait implements stack.LinkEndpoint.Wait.
|
||||||
func (e *Endpoint) Wait() {
|
func (e *Endpoint) Wait() {}
|
||||||
e.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes io.ReadWriteCloser.
|
|
||||||
func (e *Endpoint) Close() error {
|
|
||||||
return e.rwc.Close()
|
|
||||||
}
|
|
15
device/tun/opts.go
Executable file
15
device/tun/opts.go
Executable file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
13
device/tun/opts_windows.go
Executable file
13
device/tun/opts_windows.go
Executable file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
2
device/tun/tun.go
Executable file
2
device/tun/tun.go
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package tun provides TUN which implemented device.Device interface.
|
||||||
|
package tun
|
107
device/tun/tun_linux.go
Executable file
107
device/tun/tun_linux.go
Executable file
@@ -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
|
||||||
|
}
|
80
device/tun/tun_unix.go
Executable file
80
device/tun/tun_unix.go
Executable file
@@ -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()
|
||||||
|
}
|
66
device/tun/tun_windows.go
Executable file
66
device/tun/tun_windows.go
Executable file
@@ -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()
|
||||||
|
}
|
@@ -2,7 +2,7 @@ version: '2.4'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
tun2socks:
|
tun2socks:
|
||||||
image: xjasonlyu/tun2socks:latest
|
image: ghcr.io/xjasonlyu/tun2socks:latest
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
devices:
|
devices:
|
||||||
@@ -11,15 +11,17 @@ services:
|
|||||||
- GODEBUG=madvdontneed=1
|
- GODEBUG=madvdontneed=1
|
||||||
- PROXY=
|
- PROXY=
|
||||||
- LOGLEVEL=
|
- LOGLEVEL=
|
||||||
- API=
|
- STATS=
|
||||||
- DNS=
|
- SECRET=
|
||||||
- HOSTS=
|
|
||||||
- EXCLUDED=
|
- EXCLUDED=
|
||||||
- EXTRACMD=
|
- EXTRACMD=
|
||||||
networks:
|
networks:
|
||||||
switch:
|
switch:
|
||||||
ipv4_address: 172.20.1.2
|
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
|
container_name: tun2socks
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
@@ -28,7 +30,7 @@ networks:
|
|||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
- subnet: '172.20.1.0/25'
|
- subnet: '172.20.1.0/24'
|
||||||
gateway: 172.20.1.1
|
gateway: 172.20.1.1
|
||||||
driver: macvlan
|
driver: macvlan
|
||||||
driver_opts:
|
driver_opts:
|
@@ -59,18 +59,14 @@ main() {
|
|||||||
sh -c "$EXTRACMD"
|
sh -c "$EXTRACMD"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$API" ]; then
|
if [ -n "$STATS" ]; then
|
||||||
ARGS="--api $API"
|
ARGS="--stats $STATS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$DNS" ]; then
|
if [ -n "$SECRET" ]; then
|
||||||
ARGS="$ARGS --dns $DNS"
|
ARGS="$ARGS --secret $SECRET"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for item in $(echo "$HOSTS" | tr ',' '\n'); do
|
|
||||||
ARGS="$ARGS --hosts $item"
|
|
||||||
done
|
|
||||||
|
|
||||||
exec tun2socks \
|
exec tun2socks \
|
||||||
--loglevel "$LOGLEVEL" \
|
--loglevel "$LOGLEVEL" \
|
||||||
--interface "$ETH" \
|
--interface "$ETH" \
|
111
engine/engine.go
Executable file
111
engine/engine.go
Executable file
@@ -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
|
||||||
|
}
|
40
engine/opts.go
Executable file
40
engine/opts.go
Executable file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
69
engine/parse.go
Normal file
69
engine/parse.go
Normal file
@@ -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)
|
||||||
|
}
|
19
engine/tunnel.go
Normal file
19
engine/tunnel.go
Normal file
@@ -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)
|
||||||
|
}
|
19
go.mod
19
go.mod
@@ -7,18 +7,17 @@ require (
|
|||||||
github.com/go-chi/chi v4.1.2+incompatible
|
github.com/go-chi/chi v4.1.2+incompatible
|
||||||
github.com/go-chi/cors v1.1.1
|
github.com/go-chi/cors v1.1.1
|
||||||
github.com/go-chi/render v1.0.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/gorilla/websocket v1.4.2
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6
|
github.com/stretchr/testify v1.5.1
|
||||||
go.uber.org/atomic v1.7.0
|
go.uber.org/atomic v1.7.0
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||||
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||||
golang.zx2c4.com/wireguard v0.0.20200320
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
golang.zx2c4.com/wireguard v0.0.20201118
|
||||||
gvisor.dev/gvisor v0.0.0-20201107072535-9e848922ed33
|
gvisor.dev/gvisor v0.0.0-20210205020539-1dbf4e4527a5
|
||||||
)
|
)
|
||||||
|
312
go.sum
312
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.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
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.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.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.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.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.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=
|
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 v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
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/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 h1:PysSf9sLT3Qn8jhlin5v7Rk68gOQG4K5BZFY1nxLGxI=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.6/go.mod h1:LSXCjyHesPY3pLjhwff1mQX72ItcBT/N2xNC685cYeU=
|
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/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
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/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/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=
|
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/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/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.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/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/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/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/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||||
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY=
|
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/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/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/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.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 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-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
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-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/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/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/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/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/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.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.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.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/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/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=
|
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/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 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
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-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-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/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/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/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/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/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/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 v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
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/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.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/gogo/protobuf v1.3.1/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-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-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-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.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.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.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 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.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.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.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.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/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.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.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-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.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
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 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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
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.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.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.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.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-cmp v0.5.1/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.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/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 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/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 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-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-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-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/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/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.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.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.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/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=
|
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/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/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/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.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/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/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-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/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
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=
|
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/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/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/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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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=
|
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/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 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/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/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/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/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.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/opencontainers/runtime-spec v1.0.2/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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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/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/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/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 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.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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
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=
|
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/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 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.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/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.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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/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 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
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/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/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6 h1:iQsLkjayjJs29VOeXaeznpy1jddiuRjstj6MfFbKVoM=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xjasonlyu/clash v0.15.1-0.20201105074459-aa45c8b56cf6/go.mod h1:eQ5eRTAjXlzRV4rB9pOujCwDl1mBF8htrMvKMqavqvo=
|
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=
|
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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
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.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 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
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-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-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-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-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-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-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-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-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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-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-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-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-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-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-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/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=
|
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-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-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-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-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-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/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.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.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.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.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-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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-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-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-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-20190628185345-da137c7871d7/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-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-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-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 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/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 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
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-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-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-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-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-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-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-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-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-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-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 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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=
|
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-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-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-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-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-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-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 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-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-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-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-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-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-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-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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
|
golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s=
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/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-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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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=
|
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-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 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-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-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-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-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-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-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-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-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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU=
|
||||||
golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY=
|
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.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.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.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
google.golang.org/api v0.9.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.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.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.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.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.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
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-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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/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-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-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-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-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-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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
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.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
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-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-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 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.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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
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=
|
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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
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/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.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.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.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=
|
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-20210205020539-1dbf4e4527a5 h1:rXHdBmJgbj9e4FQHMUfvH+/QDIeh+GJT11TcOslPP30=
|
||||||
gvisor.dev/gvisor v0.0.0-20201107072535-9e848922ed33/go.mod h1:t4GUXJhnQEPtYSvrvRNW5CNdBQ2oWZBy7P+FbCVKBFY=
|
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-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-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-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.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-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/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.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
|
||||||
k8s.io/apimachinery v0.16.14-rc.0/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/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=
|
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/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/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=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
@@ -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}
|
|
||||||
}
|
|
@@ -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
|
|
||||||
}
|
|
@@ -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",
|
|
||||||
}
|
|
||||||
)
|
|
@@ -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),
|
|
||||||
)
|
|
||||||
}
|
|
@@ -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
|
|
@@ -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
|
|
||||||
}
|
|
@@ -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)
|
|
||||||
}
|
|
@@ -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
|
|
||||||
}
|
|
@@ -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")
|
|
||||||
}
|
|
@@ -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()
|
|
||||||
}
|
|
@@ -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))
|
|
||||||
}
|
|
@@ -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
|
|
||||||
}
|
|
2
pkg/log/event.go → log/event.go
Normal file → Executable file
2
pkg/log/event.go → log/event.go
Normal file → Executable file
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/common/observable"
|
"github.com/xjasonlyu/tun2socks/common/observable"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
5
pkg/log/level.go → log/level.go
Normal file → Executable file
5
pkg/log/level.go → log/level.go
Normal file → Executable file
@@ -10,7 +10,6 @@ type Level uint32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SilentLevel Level = iota
|
SilentLevel Level = iota
|
||||||
FatalLevel
|
|
||||||
ErrorLevel
|
ErrorLevel
|
||||||
WarnLevel
|
WarnLevel
|
||||||
InfoLevel
|
InfoLevel
|
||||||
@@ -48,8 +47,6 @@ func (level Level) String() string {
|
|||||||
return "warning"
|
return "warning"
|
||||||
case ErrorLevel:
|
case ErrorLevel:
|
||||||
return "error"
|
return "error"
|
||||||
case FatalLevel:
|
|
||||||
return "fatal"
|
|
||||||
case SilentLevel:
|
case SilentLevel:
|
||||||
return "silent"
|
return "silent"
|
||||||
default:
|
default:
|
||||||
@@ -61,8 +58,6 @@ func ParseLevel(lvl string) (Level, error) {
|
|||||||
switch strings.ToLower(lvl) {
|
switch strings.ToLower(lvl) {
|
||||||
case "silent", "none":
|
case "silent", "none":
|
||||||
return SilentLevel, nil
|
return SilentLevel, nil
|
||||||
case "fatal":
|
|
||||||
return FatalLevel, nil
|
|
||||||
case "error":
|
case "error":
|
||||||
return ErrorLevel, nil
|
return ErrorLevel, nil
|
||||||
case "warn", "warning":
|
case "warn", "warning":
|
2
pkg/log/log.go → log/log.go
Normal file → Executable file
2
pkg/log/log.go → log/log.go
Normal file → Executable file
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// defaultLevel is package default loglevel.
|
// defaultLevel is package default logging level.
|
||||||
defaultLevel = InfoLevel
|
defaultLevel = InfoLevel
|
||||||
)
|
)
|
||||||
|
|
90
main.go
Normal file → Executable file
90
main.go
Normal file → Executable file
@@ -1,43 +1,71 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"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"
|
flag "github.com/spf13/pflag"
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const About = "A tun2socks powered by gVisor TCP/IP stack."
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version string
|
device string
|
||||||
BuildTime string
|
iface string
|
||||||
|
level string
|
||||||
|
proxy string
|
||||||
|
secret string
|
||||||
|
stats string
|
||||||
|
mtu int
|
||||||
|
version bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func init() {
|
||||||
app := &cli.App{
|
flag.StringVarP(&device, "device", "d", "", "Use this device [driver://]name")
|
||||||
Usage: About,
|
flag.StringVarP(&iface, "interface", "i", "", "Use network INTERFACE (Darwin/Linux only)")
|
||||||
Version: Version,
|
flag.StringVarP(&proxy, "proxy", "p", "", "Use this proxy [protocol://]host[:port]")
|
||||||
Action: cmd.Main,
|
flag.StringVarP(&level, "loglevel", "l", "info", "Log level [debug|info|warn|error|silent]")
|
||||||
Flags: []cli.Flag{
|
flag.StringVar(&secret, "secret", "", "HTTP statistic server auth secret")
|
||||||
&cmd.API,
|
flag.StringVar(&stats, "stats", "", "HTTP statistic server listen address")
|
||||||
&cmd.Device,
|
flag.IntVarP(&mtu, "mtu", "m", 0, "Maximum transmission unit")
|
||||||
&cmd.DNS,
|
flag.BoolVarP(&version, "version", "v", false, "Show version information and quit")
|
||||||
&cmd.Hosts,
|
flag.Parse()
|
||||||
&cmd.Interface,
|
}
|
||||||
&cmd.LogLevel,
|
|
||||||
&cmd.Proxy,
|
func main() {
|
||||||
&cmd.Version,
|
if version {
|
||||||
},
|
fmt.Printf("%s %s\n%s/%s, %s, %s\n",
|
||||||
HideVersion: true,
|
constant.Name,
|
||||||
HideHelpCommand: true,
|
constant.Version,
|
||||||
}
|
runtime.GOOS,
|
||||||
app.Compiled, _ = time.Parse(time.RFC3339, BuildTime)
|
runtime.GOARCH,
|
||||||
|
runtime.Version(),
|
||||||
if err := app.Run(os.Args); err != nil {
|
constant.BuildTime,
|
||||||
log.Fatalf("Failed to start: %v", err)
|
)
|
||||||
}
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
@@ -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))
|
|
||||||
}
|
|
@@ -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)
|
|
||||||
}
|
|
@@ -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
|
|
||||||
}
|
|
@@ -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()
|
|
||||||
}
|
|
29
proxy/base.go
Executable file
29
proxy/base.go
Executable file
@@ -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")
|
||||||
|
}
|
@@ -3,22 +3,17 @@ package proxy
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/component/dialer"
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
"github.com/xjasonlyu/tun2socks/component/dialer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Direct struct {
|
type Direct struct {
|
||||||
*Base
|
*Base
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDirect(url *url.URL) (*Direct, error) {
|
func NewDirect() *Direct {
|
||||||
return &Direct{
|
return &Direct{}
|
||||||
Base: &Base{
|
|
||||||
url: url,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Direct) DialContext(ctx context.Context, metadata *adapter.Metadata) (net.Conn, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tcpKeepAlive(c)
|
setKeepAlive(c)
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +37,12 @@ type directPacketConn struct {
|
|||||||
net.PacketConn
|
net.PacketConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (_ int, err error) {
|
func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||||
var udpAddr *net.UDPAddr
|
if m, ok := addr.(*adapter.Metadata); ok && m.DstIP != nil {
|
||||||
if m, ok := addr.(*adapter.Metadata); ok && m.Host == "" {
|
return pc.PacketConn.WriteTo(b, m.UDPAddr())
|
||||||
udpAddr = m.UDPAddr()
|
|
||||||
} else {
|
|
||||||
udpAddr, err = resolveUDPAddr("udp", addr.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
18
proxy/keepalive.go
Executable file
18
proxy/keepalive.go
Executable file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
45
proxy/proxy.go
Executable file
45
proxy/proxy.go
Executable file
@@ -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)
|
||||||
|
}
|
@@ -5,13 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"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/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 {
|
type ShadowSocks struct {
|
||||||
@@ -20,16 +19,14 @@ type ShadowSocks struct {
|
|||||||
cipher core.Cipher
|
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)
|
cipher, err := core.PickCipher(method, nil, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ss initialize: %w", err)
|
return nil, fmt.Errorf("ss initialize: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ShadowSocks{
|
return &ShadowSocks{
|
||||||
Base: &Base{
|
Base: NewBase(addr),
|
||||||
url: url,
|
|
||||||
},
|
|
||||||
cipher: cipher,
|
cipher: cipher,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -39,10 +36,10 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *adapter.Metada
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||||
}
|
}
|
||||||
tcpKeepAlive(c)
|
setKeepAlive(c)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil && c != nil {
|
||||||
c.Close()
|
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) {
|
func (ss *ShadowSocks) DialUDP(_ *adapter.Metadata) (net.PacketConn, error) {
|
||||||
pc, err := dialer.ListenPacket("udp", "")
|
pc, err := dialer.ListenPacket("udp", "")
|
||||||
if err != nil {
|
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 {
|
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)
|
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) {
|
func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
n, _, e := pc.PacketConn.ReadFrom(b)
|
n, _, err := pc.PacketConn.ReadFrom(b)
|
||||||
if e != nil {
|
if err != nil {
|
||||||
return 0, nil, e
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := socks5.SplitAddr(b[:n])
|
addr := socks5.SplitAddr(b[:n])
|
||||||
@@ -104,5 +101,5 @@ func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy(b, b[len(addr):])
|
copy(b, b[len(addr):])
|
||||||
return n - len(addr), udpAddr, e
|
return n - len(addr), udpAddr, err
|
||||||
}
|
}
|
@@ -6,11 +6,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/component/dialer"
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
"github.com/xjasonlyu/clash/component/socks5"
|
"github.com/xjasonlyu/tun2socks/component/dialer"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
"github.com/xjasonlyu/tun2socks/component/socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Socks5 struct {
|
type Socks5 struct {
|
||||||
@@ -20,11 +19,9 @@ type Socks5 struct {
|
|||||||
pass string
|
pass string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks5(url *url.URL, user, pass string) (*Socks5, error) {
|
func NewSocks5(addr, user, pass string) (*Socks5, error) {
|
||||||
return &Socks5{
|
return &Socks5{
|
||||||
Base: &Base{
|
Base: NewBase(addr),
|
||||||
url: url,
|
|
||||||
},
|
|
||||||
user: user,
|
user: user,
|
||||||
pass: pass,
|
pass: pass,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -35,10 +32,10 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *adapter.Metadata) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||||
}
|
}
|
||||||
tcpKeepAlive(c)
|
setKeepAlive(c)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil && c != nil {
|
||||||
c.Close()
|
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) {
|
func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", ss.Addr())
|
c, err := dialer.DialContext(ctx, "tcp", ss.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
err = fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tcpKeepAlive(c)
|
setKeepAlive(c)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil && c != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -96,7 +94,7 @@ func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) {
|
|||||||
|
|
||||||
pc, err := dialer.ListenPacket("udp", "")
|
pc, err := dialer.ListenPacket("udp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, fmt.Errorf("listen packet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -108,12 +106,11 @@ func (ss *Socks5) DialUDP(_ *adapter.Metadata) (_ net.PacketConn, err error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
bindAddr := addr.UDPAddr()
|
bindAddr := addr.UDPAddr()
|
||||||
if bindAddr.IP.IsUnspecified() {
|
if bindAddr.IP.IsUnspecified() { /* e.g. "0.0.0.0" or "::" */
|
||||||
udpAddr, err := resolveUDPAddr("udp", ss.Addr())
|
udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindAddr.IP = udpAddr.IP
|
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) {
|
func (pc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
n, _, e := pc.PacketConn.ReadFrom(b)
|
n, _, err := pc.PacketConn.ReadFrom(b)
|
||||||
if e != nil {
|
if err != nil {
|
||||||
return 0, nil, e
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, payload, err := socks5.DecodeUDPPacket(b)
|
addr, payload, err := socks5.DecodeUDPPacket(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
@@ -153,7 +151,7 @@ func (pc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|||||||
|
|
||||||
udpAddr := addr.UDPAddr()
|
udpAddr := addr.UDPAddr()
|
||||||
if udpAddr == nil {
|
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
|
// due to DecodeUDPPacket is mutable, record addr length
|
10
stack/handler.go
Executable file
10
stack/handler.go
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Add(adapter.TCPConn)
|
||||||
|
AddPacket(adapter.UDPPacket)
|
||||||
|
}
|
19
internal/core/icmp.go → stack/icmp.go
Normal file → Executable file
19
internal/core/icmp.go → stack/icmp.go
Normal file → Executable file
@@ -1,27 +1,24 @@
|
|||||||
package core
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type icmpHandleFunc func()
|
func withICMPHandler() Option {
|
||||||
|
return func(s *Stack) error {
|
||||||
func WithICMPHandler(_ icmpHandleFunc) Option {
|
// Add default route table for IPv4 and IPv6.
|
||||||
return func(s *stack.Stack) error {
|
|
||||||
// Add default route table for IPv4 and IPv6
|
|
||||||
// This will handle all incoming ICMP packets.
|
// This will handle all incoming ICMP packets.
|
||||||
s.SetRouteTable([]tcpip.Route{
|
s.SetRouteTable([]tcpip.Route{
|
||||||
{
|
{
|
||||||
Destination: mustSubnet("0.0.0.0/0"),
|
Destination: mustSubnet("0.0.0.0/0"),
|
||||||
NIC: defaultNICID,
|
NIC: s.nicID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Destination: mustSubnet("::/0"),
|
Destination: mustSubnet("::/0"),
|
||||||
NIC: defaultNICID,
|
NIC: s.nicID,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
@@ -32,12 +29,12 @@ func WithICMPHandler(_ icmpHandleFunc) Option {
|
|||||||
func mustSubnet(s string) tcpip.Subnet {
|
func mustSubnet(s string) tcpip.Subnet {
|
||||||
_, ipNet, err := net.ParseCIDR(s)
|
_, ipNet, err := net.ParseCIDR(s)
|
||||||
if err != nil {
|
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))
|
subnet, err := tcpip.NewSubnet(tcpip.Address(ipNet.IP), tcpip.AddressMask(ipNet.Mask))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("unable to NewSubnet(%s): %w", ipNet, err))
|
panic(fmt.Errorf("new subnet: %w", err))
|
||||||
}
|
}
|
||||||
return subnet
|
return subnet
|
||||||
}
|
}
|
28
internal/core/nic.go → stack/nic.go
Normal file → Executable file
28
internal/core/nic.go → stack/nic.go
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
package core
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// defaultNICID is the ID of default NIC used by DefaultStack.
|
// 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
|
// nicPromiscuousModeEnabled is the value used by stack to enable
|
||||||
// or disable NIC's promiscuous mode.
|
// or disable NIC's promiscuous mode.
|
||||||
@@ -20,31 +20,31 @@ const (
|
|||||||
nicSpoofingEnabled = true
|
nicSpoofingEnabled = true
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithCreatingNIC creates NIC for stack.
|
// withCreatingNIC creates NIC for stack.
|
||||||
func WithCreatingNIC(nicID tcpip.NICID, ep stack.LinkEndpoint) Option {
|
func withCreatingNIC(ep stack.LinkEndpoint) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
if err := s.CreateNIC(nicID, ep); err != nil {
|
if err := s.CreateNIC(s.nicID, ep); err != nil {
|
||||||
return fmt.Errorf("create NIC: %s", err)
|
return fmt.Errorf("create NIC: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPromiscuousMode sets promiscuous mode in the given NIC.
|
// withPromiscuousMode sets promiscuous mode in the given NIC.
|
||||||
func WithPromiscuousMode(nicID tcpip.NICID, v bool) Option {
|
func withPromiscuousMode(v bool) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
if err := s.SetPromiscuousMode(nicID, v); err != nil {
|
if err := s.SetPromiscuousMode(s.nicID, v); err != nil {
|
||||||
return fmt.Errorf("set promiscuous mode: %s", err)
|
return fmt.Errorf("set promiscuous mode: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
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.
|
// endpoints to bind to any address in the NIC.
|
||||||
func WithSpoofing(nicID tcpip.NICID, v bool) Option {
|
func withSpoofing(v bool) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
if err := s.SetSpoofing(nicID, v); err != nil {
|
if err := s.SetSpoofing(s.nicID, v); err != nil {
|
||||||
return fmt.Errorf("set spoofing: %s", err)
|
return fmt.Errorf("set spoofing: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
62
internal/core/opts.go → stack/opts.go
Normal file → Executable file
62
internal/core/opts.go → stack/opts.go
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
package core
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,11 +53,50 @@ const (
|
|||||||
tcpSACKEnabled = true
|
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.
|
// WithDefaultTTL sets the default TTL used by stack.
|
||||||
func WithDefaultTTL(ttl uint8) Option {
|
func WithDefaultTTL(ttl uint8) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
opt := tcpip.DefaultTTLOption(ttl)
|
opt := tcpip.DefaultTTLOption(ttl)
|
||||||
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
|
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set ipv4 default TTL: %s", err)
|
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.
|
// WithForwarding sets packet forwarding between NICs for IPv4 & IPv6.
|
||||||
func WithForwarding(v bool) Option {
|
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 {
|
if err := s.SetForwarding(ipv4.ProtocolNumber, v); err != nil {
|
||||||
return fmt.Errorf("set ipv4 forwarding: %s", err)
|
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
|
// WithICMPBurst sets the number of ICMP messages that can be sent
|
||||||
// in a single burst.
|
// in a single burst.
|
||||||
func WithICMPBurst(burst int) Option {
|
func WithICMPBurst(burst int) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
s.SetICMPBurst(burst)
|
s.SetICMPBurst(burst)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -95,7 +133,7 @@ func WithICMPBurst(burst int) Option {
|
|||||||
// WithICMPLimit sets the maximum number of ICMP messages permitted
|
// WithICMPLimit sets the maximum number of ICMP messages permitted
|
||||||
// by rate limiter.
|
// by rate limiter.
|
||||||
func WithICMPLimit(limit rate.Limit) Option {
|
func WithICMPLimit(limit rate.Limit) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
s.SetICMPLimit(limit)
|
s.SetICMPLimit(limit)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -103,7 +141,7 @@ func WithICMPLimit(limit rate.Limit) Option {
|
|||||||
|
|
||||||
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
|
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
|
||||||
func WithTCPBufferSizeRange(a, b, c int) Option {
|
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}
|
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
|
||||||
return fmt.Errorf("set TCP receive buffer size range: %s", err)
|
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.
|
// WithTCPCongestionControl sets the current congestion control algorithm.
|
||||||
func WithTCPCongestionControl(cc string) Option {
|
func WithTCPCongestionControl(cc string) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
opt := tcpip.CongestionControlOption(cc)
|
opt := tcpip.CongestionControlOption(cc)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP congestion control algorithm: %s", err)
|
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.
|
// WithTCPDelay enables or disables Nagle's algorithm in TCP.
|
||||||
func WithTCPDelay(v bool) Option {
|
func WithTCPDelay(v bool) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
opt := tcpip.TCPDelayEnabled(v)
|
opt := tcpip.TCPDelayEnabled(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP delay: %s", err)
|
return fmt.Errorf("set TCP delay: %s", err)
|
||||||
@@ -140,7 +178,7 @@ func WithTCPDelay(v bool) Option {
|
|||||||
|
|
||||||
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
|
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
|
||||||
func WithTCPModerateReceiveBuffer(v bool) Option {
|
func WithTCPModerateReceiveBuffer(v bool) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
opt := tcpip.TCPModerateReceiveBufferOption(v)
|
opt := tcpip.TCPModerateReceiveBufferOption(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP moderate receive buffer: %s", err)
|
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.
|
// WithTCPSACKEnabled sets the SACK option for TCP.
|
||||||
func WithTCPSACKEnabled(v bool) Option {
|
func WithTCPSACKEnabled(v bool) Option {
|
||||||
return func(s *stack.Stack) error {
|
return func(s *Stack) error {
|
||||||
opt := tcpip.TCPSACKEnabled(v)
|
opt := tcpip.TCPSACKEnabled(v)
|
||||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||||
return fmt.Errorf("set TCP SACK: %s", err)
|
return fmt.Errorf("set TCP SACK: %s", err)
|
80
stack/stack.go
Executable file
80
stack/stack.go
Executable file
@@ -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
|
||||||
|
}
|
47
internal/core/tcp.go → stack/tcp.go
Normal file → Executable file
47
internal/core/tcp.go → stack/tcp.go
Normal file → Executable file
@@ -1,18 +1,16 @@
|
|||||||
package core
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
"gvisor.dev/gvisor/pkg/waiter"
|
"gvisor.dev/gvisor/pkg/waiter"
|
||||||
|
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -35,25 +33,20 @@ const (
|
|||||||
tcpKeepaliveInterval = 30 * time.Second
|
tcpKeepaliveInterval = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type tcpHandleFunc func(adapter.TCPConn)
|
func withTCPHandler() Option {
|
||||||
|
return func(s *Stack) error {
|
||||||
func WithTCPHandler(handle tcpHandleFunc) Option {
|
tcpForwarder := tcp.NewForwarder(s.Stack, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
|
||||||
return func(s *stack.Stack) error {
|
|
||||||
tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
|
|
||||||
var wq waiter.Queue
|
var wq waiter.Queue
|
||||||
id := r.ID()
|
id := r.ID()
|
||||||
ep, err := r.CreateEndpoint(&wq)
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("[STACK] %s create endpoint error: %v", formatID(&id), err)
|
|
||||||
// prevent potential half-open TCP connection leak.
|
// prevent potential half-open TCP connection leak.
|
||||||
r.Complete(true)
|
r.Complete(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Complete(false)
|
r.Complete(false)
|
||||||
|
|
||||||
if err := setKeepalive(ep); err != nil {
|
setKeepalive(ep)
|
||||||
log.Warnf("[STACK] %s %v", formatID(&id), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := &tcpConn{
|
conn := &tcpConn{
|
||||||
Conn: gonet.NewTCPConn(&wq, ep),
|
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)
|
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||||
return nil
|
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 {
|
func setKeepalive(ep tcpip.Endpoint) error {
|
||||||
if err := ep.SetSockOptBool(tcpip.KeepaliveEnabledOption, true); err != nil {
|
ep.SocketOptions().SetKeepAlive(true)
|
||||||
return fmt.Errorf("set keepalive: %s", err)
|
|
||||||
}
|
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
|
||||||
idleOpt := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
|
if err := ep.SetSockOpt(&idle); err != nil {
|
||||||
if err := ep.SetSockOpt(&idleOpt); err != nil {
|
|
||||||
return fmt.Errorf("set keepalive idle: %s", err)
|
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 fmt.Errorf("set keepalive interval: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
196
stack/udp.go
Executable file
196
stack/udp.go
Executable file
@@ -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
|
||||||
|
}
|
18
internal/api/connections.go → stats/connections.go
Normal file → Executable file
18
internal/api/connections.go → stats/connections.go
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
package api
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -7,13 +7,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/xjasonlyu/tun2socks/tunnel"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/xjasonlyu/tun2socks/internal/manager"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultInterval = 1000
|
||||||
|
|
||||||
func connectionRouter() http.Handler {
|
func connectionRouter() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Get("/", getConnections)
|
r.Get("/", getConnections)
|
||||||
@@ -24,7 +26,7 @@ func connectionRouter() http.Handler {
|
|||||||
|
|
||||||
func getConnections(w http.ResponseWriter, r *http.Request) {
|
func getConnections(w http.ResponseWriter, r *http.Request) {
|
||||||
if !websocket.IsWebSocketUpgrade(r) {
|
if !websocket.IsWebSocketUpgrade(r) {
|
||||||
snapshot := manager.DefaultManager.Snapshot()
|
snapshot := tunnel.DefaultManager.Snapshot()
|
||||||
render.JSON(w, r, snapshot)
|
render.JSON(w, r, snapshot)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
intervalStr := r.URL.Query().Get("interval")
|
intervalStr := r.URL.Query().Get("interval")
|
||||||
interval := 1000
|
interval := defaultInterval
|
||||||
if intervalStr != "" {
|
if intervalStr != "" {
|
||||||
t, err := strconv.Atoi(intervalStr)
|
t, err := strconv.Atoi(intervalStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,7 +52,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) {
|
|||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
sendSnapshot := func() error {
|
sendSnapshot := func() error {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
snapshot := manager.DefaultManager.Snapshot()
|
snapshot := tunnel.DefaultManager.Snapshot()
|
||||||
if err := json.NewEncoder(buf).Encode(snapshot); err != nil {
|
if err := json.NewEncoder(buf).Encode(snapshot); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -73,7 +75,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func closeConnection(w http.ResponseWriter, r *http.Request) {
|
func closeConnection(w http.ResponseWriter, r *http.Request) {
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
snapshot := manager.DefaultManager.Snapshot()
|
snapshot := tunnel.DefaultManager.Snapshot()
|
||||||
for _, c := range snapshot.Connections {
|
for _, c := range snapshot.Connections {
|
||||||
if id == c.ID() {
|
if id == c.ID() {
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
@@ -84,7 +86,7 @@ func closeConnection(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func closeAllConnections(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 {
|
for _, c := range snapshot.Connections {
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
}
|
}
|
21
stats/errors.go
Executable file
21
stats/errors.go
Executable file
@@ -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}
|
||||||
|
}
|
110
internal/api/server.go → stats/server.go
Normal file → Executable file
110
internal/api/server.go → stats/server.go
Normal file → Executable file
@@ -1,32 +1,24 @@
|
|||||||
package api
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/chi"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/gorilla/websocket"
|
"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 (
|
var (
|
||||||
serverApp *cli.App
|
|
||||||
serverSecret = ""
|
|
||||||
serverAddr = ""
|
|
||||||
|
|
||||||
upgrader = websocket.Upgrader{
|
upgrader = websocket.Upgrader{
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
@@ -34,23 +26,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start(apiURL string, app *cli.App) error {
|
func Start(addr, secret string) 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")
|
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
c := cors.New(cors.Options{
|
c := cors.New(cors.Options{
|
||||||
@@ -61,10 +37,8 @@ func Start(apiURL string, app *cli.App) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
r.Use(c.Handler)
|
r.Use(c.Handler)
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(authentication)
|
r.Use(authenticator(secret))
|
||||||
|
|
||||||
r.Get("/", hello)
|
r.Get("/", hello)
|
||||||
r.Get("/logs", getLogs)
|
r.Get("/logs", getLogs)
|
||||||
r.Get("/traffic", traffic)
|
r.Get("/traffic", traffic)
|
||||||
@@ -72,7 +46,7 @@ func Start(apiURL string, app *cli.App) error {
|
|||||||
r.Mount("/connections", connectionRouter())
|
r.Mount("/connections", connectionRouter())
|
||||||
})
|
})
|
||||||
|
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddr)
|
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -82,45 +56,47 @@ func Start(apiURL string, app *cli.App) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
return http.Serve(listener, r)
|
||||||
_ = http.Serve(listener, r)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func authentication(next http.Handler) http.Handler {
|
func hello(w http.ResponseWriter, r *http.Request) {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
render.JSON(w, r, render.M{"hello": constant.Name})
|
||||||
if serverSecret == "" {
|
}
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browser websocket not support custom header
|
func authenticator(secret string) func(http.Handler) http.Handler {
|
||||||
if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" {
|
return func(next http.Handler) http.Handler {
|
||||||
token := r.URL.Query().Get("token")
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
if token != serverSecret {
|
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.Status(r, http.StatusUnauthorized)
|
||||||
render.JSON(w, r, ErrUnauthorized)
|
render.JSON(w, r, ErrUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogs(w http.ResponseWriter, r *http.Request) {
|
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 {
|
type Traffic struct {
|
||||||
Up int64 `json:"up"`
|
Up int64 `json:"up"`
|
||||||
Down int64 `json:"down"`
|
Down int64 `json:"down"`
|
||||||
@@ -204,7 +176,7 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
t := manager.DefaultManager
|
t := tunnel.DefaultManager
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
var err error
|
var err error
|
||||||
for range tick.C {
|
for range tick.C {
|
||||||
@@ -231,5 +203,5 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func version(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})
|
||||||
}
|
}
|
4
stats/stats.go
Normal file
4
stats/stats.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Package stats provides statistic data via http server.
|
||||||
|
package stats
|
||||||
|
|
||||||
|
// Ref: github.com/Dreamacro/clash/hub/route
|
22
tunnel/manager.go
Executable file
22
tunnel/manager.go
Executable file
@@ -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)
|
||||||
|
}
|
28
internal/tunnel/tcp.go → tunnel/tcp.go
Normal file → Executable file
28
internal/tunnel/tcp.go → tunnel/tcp.go
Normal file → Executable file
@@ -7,16 +7,14 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/common/pool"
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
"github.com/xjasonlyu/tun2socks/common/pool"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/manager"
|
"github.com/xjasonlyu/tun2socks/log"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/proxy"
|
"github.com/xjasonlyu/tun2socks/proxy"
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tcpWaitTimeout = 5 * time.Second
|
tcpWaitTimeout = 5 * time.Second
|
||||||
relayBufferSize = pool.RelayBufferSize
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleTCP(localConn adapter.TCPConn) {
|
func handleTCP(localConn adapter.TCPConn) {
|
||||||
@@ -28,12 +26,6 @@ func handleTCP(localConn adapter.TCPConn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := resolveMetadata(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("[Metadata] resolve metadata error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
targetConn, err := proxy.Dial(metadata)
|
targetConn, err := proxy.Dial(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("[TCP] dial %s error: %v", metadata.DestinationAddress(), err)
|
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 {
|
if dialerAddr, ok := targetConn.LocalAddr().(*net.TCPAddr); ok {
|
||||||
metadata.MidIP = dialerAddr.IP
|
metadata.MidIP = dialerAddr.IP
|
||||||
metadata.MidPort = uint16(dialerAddr.Port)
|
metadata.MidPort = uint16(dialerAddr.Port)
|
||||||
} else {
|
} else { /* fallback */
|
||||||
ip, p, _ := net.SplitHostPort(targetConn.LocalAddr().String())
|
ip, p, _ := net.SplitHostPort(targetConn.LocalAddr().String())
|
||||||
port, _ := strconv.ParseUint(p, 10, 16)
|
port, _ := strconv.ParseUint(p, 10, 16)
|
||||||
metadata.MidIP = net.ParseIP(ip)
|
metadata.MidIP = net.ParseIP(ip)
|
||||||
metadata.MidPort = uint16(port)
|
metadata.MidPort = uint16(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetConn = manager.NewTCPTracker(targetConn, metadata)
|
targetConn = newTCPTracker(targetConn, metadata)
|
||||||
defer targetConn.Close()
|
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(localConn, targetConn) /* relay connections */
|
||||||
}
|
}
|
||||||
|
|
||||||
// relay copies between left and right bidirectionally.
|
// relay copies between left and right bidirectionally.
|
||||||
func relay(left, right net.Conn) {
|
func relay(left, right net.Conn) {
|
||||||
var wg sync.WaitGroup
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -78,7 +70,7 @@ func relay(left, right net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func copyBuffer(dst io.Writer, src io.Reader) error {
|
func copyBuffer(dst io.Writer, src io.Reader) error {
|
||||||
buf := pool.Get(relayBufferSize)
|
buf := pool.Get(pool.RelayBufferSize)
|
||||||
defer pool.Put(buf)
|
defer pool.Put(buf)
|
||||||
|
|
||||||
_, err := io.CopyBuffer(dst, src, buf)
|
_, err := io.CopyBuffer(dst, src, buf)
|
31
internal/tunnel/tunnel.go → tunnel/tunnel.go
Normal file → Executable file
31
internal/tunnel/tunnel.go → tunnel/tunnel.go
Normal file → Executable file
@@ -3,8 +3,8 @@ package tunnel
|
|||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/log"
|
"github.com/xjasonlyu/tun2socks/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -15,17 +15,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
numUDPWorkers = max(runtime.NumCPU(), 4 /* at least 4 workers */)
|
|
||||||
|
|
||||||
tcpQueue = make(chan adapter.TCPConn) /* unbuffered */
|
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() {
|
func init() {
|
||||||
for i := 0; i < numUDPWorkers; i++ {
|
|
||||||
udpMultiQueue = append(udpMultiQueue, make(chan adapter.UDPPacket, maxUDPQueueSize))
|
|
||||||
}
|
|
||||||
|
|
||||||
go process()
|
go process()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,23 +31,23 @@ func Add(conn adapter.TCPConn) {
|
|||||||
|
|
||||||
// AddPacket adds udpPacket to udpQueue.
|
// AddPacket adds udpPacket to udpQueue.
|
||||||
func AddPacket(packet adapter.UDPPacket) {
|
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 {
|
select {
|
||||||
case udpMultiQueue[i] <- packet:
|
case udpQueue <- packet:
|
||||||
default:
|
default:
|
||||||
log.Warnf("queue is currently full, packet will be dropped")
|
log.Warnf("queue is currently full, packet will be dropped")
|
||||||
packet.Drop()
|
packet.Drop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func process() {
|
func process() {
|
||||||
for _, udpQueue := range udpMultiQueue {
|
for i := 0; i < numUDPWorkers; i++ {
|
||||||
queue := udpQueue
|
queue := udpQueue
|
||||||
go func() {
|
go func() {
|
||||||
for packet := range queue {
|
for packet := range queue {
|
44
internal/tunnel/udp.go → tunnel/udp.go
Normal file → Executable file
44
internal/tunnel/udp.go → tunnel/udp.go
Normal file → Executable file
@@ -7,18 +7,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xjasonlyu/clash/common/pool"
|
"github.com/xjasonlyu/tun2socks/common/adapter"
|
||||||
"github.com/xjasonlyu/clash/component/resolver"
|
"github.com/xjasonlyu/tun2socks/common/pool"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/adapter"
|
"github.com/xjasonlyu/tun2socks/component/nat"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/manager"
|
"github.com/xjasonlyu/tun2socks/log"
|
||||||
"github.com/xjasonlyu/tun2socks/internal/proxy"
|
"github.com/xjasonlyu/tun2socks/proxy"
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/log"
|
|
||||||
"github.com/xjasonlyu/tun2socks/pkg/nat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
udpTimeout = 30 * time.Second
|
udpSessionTimeout = 30 * time.Second
|
||||||
udpBufferSize = (1 << 16) - 1 // largest possible UDP datagram
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -34,18 +31,9 @@ func handleUDP(packet adapter.UDPPacket) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a fAddr if request ip is fake ip.
|
generateNATKey := func(m *adapter.Metadata) string {
|
||||||
var fAddr net.Addr
|
return m.SourceAddress() /* Full Cone NAT Key */
|
||||||
if resolver.IsExistFakeIP(metadata.DstIP) {
|
|
||||||
fAddr = metadata.UDPAddr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := resolveMetadata(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("[Metadata] resolve metadata error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key := generateNATKey(metadata)
|
key := generateNATKey(metadata)
|
||||||
|
|
||||||
handle := func(drop bool) bool {
|
handle := func(drop bool) bool {
|
||||||
@@ -86,21 +74,21 @@ func handleUDP(packet adapter.UDPPacket) {
|
|||||||
if dialerAddr, ok := pc.LocalAddr().(*net.UDPAddr); ok {
|
if dialerAddr, ok := pc.LocalAddr().(*net.UDPAddr); ok {
|
||||||
metadata.MidIP = dialerAddr.IP
|
metadata.MidIP = dialerAddr.IP
|
||||||
metadata.MidPort = uint16(dialerAddr.Port)
|
metadata.MidPort = uint16(dialerAddr.Port)
|
||||||
} else {
|
} else { /* fallback */
|
||||||
ip, p, _ := net.SplitHostPort(pc.LocalAddr().String())
|
ip, p, _ := net.SplitHostPort(pc.LocalAddr().String())
|
||||||
port, _ := strconv.ParseUint(p, 10, 16)
|
port, _ := strconv.ParseUint(p, 10, 16)
|
||||||
metadata.MidIP = net.ParseIP(ip)
|
metadata.MidIP = net.ParseIP(ip)
|
||||||
metadata.MidPort = uint16(port)
|
metadata.MidPort = uint16(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pc = manager.NewUDPTracker(pc, metadata)
|
pc = newUDPTracker(pc, metadata)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer pc.Close()
|
defer pc.Close()
|
||||||
defer packet.Drop()
|
defer packet.Drop()
|
||||||
defer natTable.Delete(key)
|
defer natTable.Delete(key)
|
||||||
|
|
||||||
handleUDPToLocal(packet, pc, fAddr, udpTimeout)
|
handleUDPToLocal(packet, pc, udpSessionTimeout)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
natTable.Set(key, pc)
|
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)
|
log.Infof("[UDP] %s --> %s", packet.RemoteAddr(), remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, fAddr net.Addr, timeout time.Duration) {
|
func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, timeout time.Duration) {
|
||||||
buf := pool.Get(udpBufferSize)
|
buf := pool.Get(pool.MaxSegmentSize)
|
||||||
defer pool.Put(buf)
|
defer pool.Put(buf)
|
||||||
|
|
||||||
for /* just loop */ {
|
for /* just loop */ {
|
||||||
@@ -131,15 +119,11 @@ func handleUDPToLocal(packet adapter.UDPPacket, pc net.PacketConn, fAddr net.Add
|
|||||||
n, from, err := pc.ReadFrom(buf)
|
n, from, err := pc.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, os.ErrDeadlineExceeded) /* ignore i/o timeout */ {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if fAddr != nil {
|
|
||||||
from = fAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := packet.WriteBack(buf[:n], from); err != nil {
|
if _, err := packet.WriteBack(buf[:n], from); err != nil {
|
||||||
log.Warnf("[UDP] write back from %s error: %v", from, err)
|
log.Warnf("[UDP] write back from %s error: %v", from, err)
|
||||||
return
|
return
|
Reference in New Issue
Block a user