Code: refresh code
This commit is contained in:
parent
3ca5d17c40
commit
d7732f6ebc
104 changed files with 11329 additions and 136 deletions
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -2,7 +2,7 @@ name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, dev ]
|
branches: [ rm ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
|
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
@ -2,7 +2,7 @@ name: Publish Docker Image
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- rm
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
jobs:
|
jobs:
|
||||||
|
|
31
.github/workflows/go.yml
vendored
31
.github/workflows/go.yml
vendored
|
@ -1,5 +1,5 @@
|
||||||
name: Go
|
name: Go
|
||||||
on: [push, pull_request]
|
on: [push]
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
@ -30,12 +30,24 @@ jobs:
|
||||||
staticcheck -- $(go list ./...)
|
staticcheck -- $(go list ./...)
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
#if: startsWith(github.ref, 'refs/tags/')
|
||||||
env:
|
env:
|
||||||
NAME: clash
|
NAME: clash
|
||||||
BINDIR: bin
|
BINDIR: bin
|
||||||
run: make -j releases
|
run: make -j releases
|
||||||
|
|
||||||
|
- name: Prepare upload
|
||||||
|
run: |
|
||||||
|
echo "FILE_DATE=_$(date +"%Y%m%d%H%M")" >> $GITHUB_ENV
|
||||||
|
echo "FILE_SHA=$(git describe --tags --always 2>/dev/null)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload files to Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: clash_${{ env.FILE_SHA }}${{ env.FILE_DATE }}
|
||||||
|
path: |
|
||||||
|
bin/*
|
||||||
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
@ -44,3 +56,18 @@ jobs:
|
||||||
with:
|
with:
|
||||||
files: bin/*
|
files: bin/*
|
||||||
draft: true
|
draft: true
|
||||||
|
|
||||||
|
- name: Delete workflow runs
|
||||||
|
uses: GitRML/delete-workflow-runs@main
|
||||||
|
with:
|
||||||
|
retain_days: 1
|
||||||
|
keep_minimum_runs: 2
|
||||||
|
|
||||||
|
- name: Remove old Releases
|
||||||
|
uses: dev-drprasad/delete-older-releases@v0.2.0
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') && !cancelled()
|
||||||
|
with:
|
||||||
|
keep_latest: 1
|
||||||
|
delete_tags: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
|
@ -2,8 +2,10 @@
|
||||||
name: Mark stale issues and pull requests
|
name: Mark stale issues and pull requests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
branches:
|
||||||
- cron: "30 1 * * *"
|
- rm
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -1,6 +1,6 @@
|
||||||
NAME=clash
|
NAME=clash
|
||||||
BINDIR=bin
|
BINDIR=bin
|
||||||
VERSION=$(shell git describe --tags || echo "unknown version")
|
VERSION=$(shell git describe --tags --always 2>/dev/null || date +%F)
|
||||||
BUILDTIME=$(shell date -u)
|
BUILDTIME=$(shell date -u)
|
||||||
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \
|
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \
|
||||||
-X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \
|
-X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \
|
||||||
|
@ -91,7 +91,7 @@ windows-386:
|
||||||
|
|
||||||
windows-amd64:
|
windows-amd64:
|
||||||
GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe
|
GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe
|
||||||
|
|
||||||
windows-arm32v7:
|
windows-arm32v7:
|
||||||
GOARCH=arm GOOS=windows GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe
|
GOARCH=arm GOOS=windows GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe
|
||||||
|
|
||||||
|
@ -109,4 +109,4 @@ all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST)
|
||||||
|
|
||||||
releases: $(gz_releases) $(zip_releases)
|
releases: $(gz_releases) $(zip_releases)
|
||||||
clean:
|
clean:
|
||||||
rm $(BINDIR)/*
|
rm $(BINDIR)/*
|
95
README.md
95
README.md
|
@ -28,15 +28,98 @@
|
||||||
- Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`.
|
- Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`.
|
||||||
- Comprehensive HTTP RESTful API controller
|
- Comprehensive HTTP RESTful API controller
|
||||||
|
|
||||||
## Premium Features
|
|
||||||
|
|
||||||
- TUN mode on macOS, Linux and Windows. [Doc](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device)
|
|
||||||
- Match your tunnel by [Script](https://github.com/Dreamacro/clash/wiki/premium-core-features#script)
|
|
||||||
- [Rule Provider](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers)
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki).
|
Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki).
|
||||||
|
|
||||||
|
## Advanced usage for this fork repository
|
||||||
|
### TUN configuration
|
||||||
|
Support macOS Linux and Windows.
|
||||||
|
|
||||||
|
For Windows, you should download the [Wintun](https://www.wintun.net) driver and copy `wintun.dll` into the System32 directory.
|
||||||
|
```yaml
|
||||||
|
# Enable the TUN listener
|
||||||
|
tun:
|
||||||
|
enable: true
|
||||||
|
stack: system # system or gvisor
|
||||||
|
dns-listen: 0.0.0.0:53 # additional dns server listen on TUN
|
||||||
|
auto-route: true # auto set global route
|
||||||
|
```
|
||||||
|
### Rules configuration
|
||||||
|
- Support rule `GEOSITE`
|
||||||
|
- Support rule `GEOIP` not match condition
|
||||||
|
- Support `network` condition for all rules
|
||||||
|
|
||||||
|
The `GEOSITE` and `GEOIP` databases via https://github.com/Loyalsoldier/v2ray-rules-dat
|
||||||
|
```yaml
|
||||||
|
rules:
|
||||||
|
# network condition for rules
|
||||||
|
- DOMAIN-SUFFIX,tabao.com,DIRECT,tcp
|
||||||
|
- DST-PORT,123,DIRECT,udp
|
||||||
|
|
||||||
|
# rule GEOSITE
|
||||||
|
- GEOSITE,category-ads-all,REJECT
|
||||||
|
- GEOSITE,icloud@cn,DIRECT
|
||||||
|
- GEOSITE,apple@cn,DIRECT
|
||||||
|
- GEOSITE,microsoft@cn,DIRECT
|
||||||
|
- GEOSITE,youtube,PROXY
|
||||||
|
- GEOSITE,geolocation-cn,DIRECT
|
||||||
|
#- GEOSITE,geolocation-!cn,PROXY
|
||||||
|
|
||||||
|
- GEOIP,private,DIRECT,no-resolve
|
||||||
|
- GEOIP,cn,DIRECT
|
||||||
|
|
||||||
|
# Not match condition for rule GEOIP
|
||||||
|
#- GEOIP,!cn,PROXY
|
||||||
|
|
||||||
|
- MATCH,PROXY
|
||||||
|
```
|
||||||
|
### IPTABLES auto-configuration
|
||||||
|
Only work on Linux OS who support `iptables`, Clash will auto-configuration iptables for tproxy listener when `tproxy-port` value isn't zero.
|
||||||
|
|
||||||
|
When `TPROXY` is enabled, the `TUN` must be disabled.
|
||||||
|
```yaml
|
||||||
|
# Enable the TPROXY listener
|
||||||
|
tproxy-port: 9898
|
||||||
|
# Disable the TUN listener
|
||||||
|
tun:
|
||||||
|
enable: false
|
||||||
|
```
|
||||||
|
Create user give name `clash`, run `$ sudo useradd -M clash` in command line.
|
||||||
|
|
||||||
|
Run Clash by user `clash` as a daemon.
|
||||||
|
|
||||||
|
Create the systemd configuration file at /etc/systemd/system/clash.service:
|
||||||
|
```shell
|
||||||
|
[Unit]
|
||||||
|
Description=Clash daemon, A rule-based proxy in Go.
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=clash
|
||||||
|
Group=clash
|
||||||
|
CapabilityBoundingSet=cap_net_admin
|
||||||
|
AmbientCapabilities=cap_net_admin
|
||||||
|
Restart=always
|
||||||
|
ExecStart=/usr/local/bin/clash -d /etc/clash
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
Launch clashd on system startup with:
|
||||||
|
```shell
|
||||||
|
$ systemctl enable clash
|
||||||
|
```
|
||||||
|
Launch clashd immediately with:
|
||||||
|
```shell
|
||||||
|
$ systemctl start clash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Display Process name
|
||||||
|
Add field `Process` to `Metadata` and prepare to get process name for Restful API `GET /connections`
|
||||||
|
|
||||||
|
To display process name in GUI please use https://yaling888.github.io/yacd/
|
||||||
|
|
||||||
## Premium Release
|
## Premium Release
|
||||||
[Release](https://github.com/Dreamacro/clash/releases/tag/premium)
|
[Release](https://github.com/Dreamacro/clash/releases/tag/premium)
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,10 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||||
|
|
||||||
tOption := &trojan.Option{
|
tOption := &trojan.Option{
|
||||||
Password: option.Password,
|
Password: option.Password,
|
||||||
ALPN: option.ALPN,
|
ALPN: option.ALPN,
|
||||||
ServerName: option.Server,
|
ServerName: option.Server,
|
||||||
SkipCertVerify: option.SkipCertVerify,
|
//SkipCertVerify: option.SkipCertVerify,
|
||||||
ClientSessionCache: getClientSessionCache(),
|
ClientSessionCache: getClientSessionCache(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +159,9 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
NextProtos: option.ALPN,
|
NextProtos: option.ALPN,
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
InsecureSkipVerify: tOption.SkipCertVerify,
|
//InsecureSkipVerify: tOption.SkipCertVerify,
|
||||||
ServerName: tOption.ServerName,
|
ServerName: tOption.ServerName,
|
||||||
ClientSessionCache: getClientSessionCache(),
|
ClientSessionCache: getClientSessionCache(),
|
||||||
}
|
}
|
||||||
|
|
326
adapter/outbound/vless.go
Normal file
326
adapter/outbound/vless.go
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
package outbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/transport/gun"
|
||||||
|
"github.com/Dreamacro/clash/transport/vless"
|
||||||
|
"github.com/Dreamacro/clash/transport/vmess"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vless struct {
|
||||||
|
*Base
|
||||||
|
client *vless.Client
|
||||||
|
option *VlessOption
|
||||||
|
|
||||||
|
// for gun mux
|
||||||
|
gunTLSConfig *tls.Config
|
||||||
|
gunConfig *gun.Config
|
||||||
|
transport *http2.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
type VlessOption struct {
|
||||||
|
Name string `proxy:"name"`
|
||||||
|
Server string `proxy:"server"`
|
||||||
|
Port int `proxy:"port"`
|
||||||
|
UUID string `proxy:"uuid"`
|
||||||
|
TLS bool `proxy:"tls,omitempty"`
|
||||||
|
UDP bool `proxy:"udp,omitempty"`
|
||||||
|
Network string `proxy:"network,omitempty"`
|
||||||
|
HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"`
|
||||||
|
HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"`
|
||||||
|
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
|
||||||
|
WSPath string `proxy:"ws-path,omitempty"`
|
||||||
|
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
|
||||||
|
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||||
|
ServerName string `proxy:"servername,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
|
var err error
|
||||||
|
switch v.option.Network {
|
||||||
|
case "ws":
|
||||||
|
host, port, _ := net.SplitHostPort(v.addr)
|
||||||
|
wsOpts := &vmess.WebsocketConfig{
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
Path: v.option.WSPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.option.WSHeaders) != 0 {
|
||||||
|
header := http.Header{}
|
||||||
|
for key, value := range v.option.WSHeaders {
|
||||||
|
header.Add(key, value)
|
||||||
|
}
|
||||||
|
wsOpts.Headers = header
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.option.TLS {
|
||||||
|
wsOpts.TLS = true
|
||||||
|
wsOpts.SessionCache = getClientSessionCache()
|
||||||
|
wsOpts.SkipCertVerify = v.option.SkipCertVerify
|
||||||
|
wsOpts.ServerName = v.option.ServerName
|
||||||
|
}
|
||||||
|
c, err = vmess.StreamWebsocketConn(c, wsOpts)
|
||||||
|
case "http":
|
||||||
|
// readability first, so just copy default TLS logic
|
||||||
|
if v.option.TLS {
|
||||||
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
tlsOpts := &vmess.TLSConfig{
|
||||||
|
Host: host,
|
||||||
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
|
SessionCache: getClientSessionCache(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.option.ServerName != "" {
|
||||||
|
tlsOpts.Host = v.option.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = vmess.StreamTLSConn(c, tlsOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
httpOpts := &vmess.HTTPConfig{
|
||||||
|
Host: host,
|
||||||
|
Method: v.option.HTTPOpts.Method,
|
||||||
|
Path: v.option.HTTPOpts.Path,
|
||||||
|
Headers: v.option.HTTPOpts.Headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
c = vmess.StreamHTTPConn(c, httpOpts)
|
||||||
|
case "h2":
|
||||||
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
tlsOpts := vmess.TLSConfig{
|
||||||
|
Host: host,
|
||||||
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
|
SessionCache: getClientSessionCache(),
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.option.ServerName != "" {
|
||||||
|
tlsOpts.Host = v.option.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = vmess.StreamTLSConn(c, &tlsOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h2Opts := &vmess.H2Config{
|
||||||
|
Hosts: v.option.HTTP2Opts.Host,
|
||||||
|
Path: v.option.HTTP2Opts.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = vmess.StreamH2Conn(c, h2Opts)
|
||||||
|
case "grpc":
|
||||||
|
c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig)
|
||||||
|
default:
|
||||||
|
// handle TLS
|
||||||
|
if v.option.TLS {
|
||||||
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
tlsOpts := &vmess.TLSConfig{
|
||||||
|
Host: host,
|
||||||
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
|
SessionCache: getClientSessionCache(),
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.option.ServerName != "" {
|
||||||
|
tlsOpts.Host = v.option.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = vmess.StreamTLSConn(c, tlsOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.client.StreamConn(c, parseVlessAddr(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext implements C.ProxyAdapter
|
||||||
|
func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
// gun transport
|
||||||
|
if v.transport != nil {
|
||||||
|
c, err := gun.StreamGunWithTransport(v.transport, v.gunConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer safeConnClose(c, err)
|
||||||
|
|
||||||
|
c, err = v.client.StreamConn(c, parseVlessAddr(metadata))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewConn(c, v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
|
}
|
||||||
|
tcpKeepAlive(c)
|
||||||
|
defer safeConnClose(c, err)
|
||||||
|
|
||||||
|
c, err = v.StreamConn(c, metadata)
|
||||||
|
return NewConn(c, v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialUDP implements C.ProxyAdapter
|
||||||
|
func (v *Vless) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
// vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
var c net.Conn
|
||||||
|
// gun transport
|
||||||
|
if v.transport != nil {
|
||||||
|
c, err = gun.StreamGunWithTransport(v.transport, v.gunConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer safeConnClose(c, err)
|
||||||
|
|
||||||
|
c, err = v.client.StreamConn(c, parseVlessAddr(metadata))
|
||||||
|
} else {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||||
|
defer cancel()
|
||||||
|
c, err = dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
|
}
|
||||||
|
tcpKeepAlive(c)
|
||||||
|
defer safeConnClose(c, err)
|
||||||
|
|
||||||
|
c, err = v.StreamConn(c, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("new vmess client error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr {
|
||||||
|
var addrType byte
|
||||||
|
var addr []byte
|
||||||
|
switch metadata.AddrType {
|
||||||
|
case C.AtypIPv4:
|
||||||
|
addrType = byte(vless.AtypIPv4)
|
||||||
|
addr = make([]byte, net.IPv4len)
|
||||||
|
copy(addr[:], metadata.DstIP.To4())
|
||||||
|
case C.AtypIPv6:
|
||||||
|
addrType = byte(vless.AtypIPv6)
|
||||||
|
addr = make([]byte, net.IPv6len)
|
||||||
|
copy(addr[:], metadata.DstIP.To16())
|
||||||
|
case C.AtypDomainName:
|
||||||
|
addrType = byte(vless.AtypDomainName)
|
||||||
|
addr = make([]byte, len(metadata.Host)+1)
|
||||||
|
addr[0] = byte(len(metadata.Host))
|
||||||
|
copy(addr[1:], []byte(metadata.Host))
|
||||||
|
}
|
||||||
|
|
||||||
|
port, _ := strconv.Atoi(metadata.DstPort)
|
||||||
|
return &vless.DstAddr{
|
||||||
|
UDP: metadata.NetWork == C.UDP,
|
||||||
|
AddrType: addrType,
|
||||||
|
Addr: addr,
|
||||||
|
Port: uint(port),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type vlessPacketConn struct {
|
||||||
|
net.Conn
|
||||||
|
rAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *vlessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||||
|
return uc.Conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *vlessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
|
n, err := uc.Conn.Read(b)
|
||||||
|
return n, uc.rAddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVless(option VlessOption) (*Vless, error) {
|
||||||
|
client, err := vless.NewClient(option.UUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.Network != "ws" {
|
||||||
|
option.TLS = true
|
||||||
|
option.SkipCertVerify = false
|
||||||
|
}
|
||||||
|
|
||||||
|
v := &Vless{
|
||||||
|
Base: &Base{
|
||||||
|
name: option.Name,
|
||||||
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
|
tp: C.Vless,
|
||||||
|
udp: option.UDP,
|
||||||
|
},
|
||||||
|
client: client,
|
||||||
|
option: &option,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch option.Network {
|
||||||
|
case "h2":
|
||||||
|
if len(option.HTTP2Opts.Host) == 0 {
|
||||||
|
option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, "www.example.com")
|
||||||
|
}
|
||||||
|
case "grpc":
|
||||||
|
dialFn := func(network, addr string) (net.Conn, error) {
|
||||||
|
c, err := dialer.DialContext(context.Background(), "tcp", v.addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
|
}
|
||||||
|
tcpKeepAlive(c)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
gunConfig := &gun.Config{
|
||||||
|
ServiceName: v.option.GrpcOpts.GrpcServiceName,
|
||||||
|
Host: v.option.ServerName,
|
||||||
|
}
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
ServerName: v.option.ServerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.option.ServerName == "" {
|
||||||
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
tlsConfig.ServerName = host
|
||||||
|
gunConfig.Host = host
|
||||||
|
}
|
||||||
|
|
||||||
|
v.gunTLSConfig = tlsConfig
|
||||||
|
v.gunConfig = gunConfig
|
||||||
|
v.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
|
@ -60,6 +60,13 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
proxy, err = outbound.NewVmess(*vmessOption)
|
proxy, err = outbound.NewVmess(*vmessOption)
|
||||||
|
case "vless":
|
||||||
|
vlessOption := &outbound.VlessOption{}
|
||||||
|
err = decoder.Decode(mapping, vlessOption)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
proxy, err = outbound.NewVless(*vlessOption)
|
||||||
case "snell":
|
case "snell":
|
||||||
snellOption := &outbound.SnellOption{}
|
snellOption := &outbound.SnellOption{}
|
||||||
err = decoder.Decode(mapping, snellOption)
|
err = decoder.Decode(mapping, snellOption)
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
package mmdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mmdb *geoip2.Reader
|
|
||||||
var once sync.Once
|
|
||||||
|
|
||||||
func LoadFromBytes(buffer []byte) {
|
|
||||||
once.Do(func() {
|
|
||||||
var err error
|
|
||||||
mmdb, err = geoip2.FromBytes(buffer)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Can't load mmdb: %s", err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Verify() bool {
|
|
||||||
instance, err := geoip2.Open(C.Path.MMDB())
|
|
||||||
if err == nil {
|
|
||||||
instance.Close()
|
|
||||||
}
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Instance() *geoip2.Reader {
|
|
||||||
once.Do(func() {
|
|
||||||
var err error
|
|
||||||
mmdb, err = geoip2.Open(C.Path.MMDB())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Can't load mmdb: %s", err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return mmdb
|
|
||||||
}
|
|
20
component/resolver/patch.go
Normal file
20
component/resolver/patch.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package resolver
|
||||||
|
|
||||||
|
import D "github.com/miekg/dns"
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultLocalServer LocalServer
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalServer interface {
|
||||||
|
ServeMsg(msg *D.Msg) (*D.Msg, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeMsg with a dns.Msg, return resolve dns.Msg
|
||||||
|
func ServeMsg(msg *D.Msg) (*D.Msg, error) {
|
||||||
|
if server := DefaultLocalServer; server != nil {
|
||||||
|
return server.ServeMsg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrIPNotFound
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ type Inbound struct {
|
||||||
RedirPort int `json:"redir-port"`
|
RedirPort int `json:"redir-port"`
|
||||||
TProxyPort int `json:"tproxy-port"`
|
TProxyPort int `json:"tproxy-port"`
|
||||||
MixedPort int `json:"mixed-port"`
|
MixedPort int `json:"mixed-port"`
|
||||||
|
Tun Tun `json:"tun"`
|
||||||
Authentication []string `json:"authentication"`
|
Authentication []string `json:"authentication"`
|
||||||
AllowLan bool `json:"allow-lan"`
|
AllowLan bool `json:"allow-lan"`
|
||||||
BindAddress string `json:"bind-address"`
|
BindAddress string `json:"bind-address"`
|
||||||
|
@ -80,12 +81,21 @@ type Profile struct {
|
||||||
StoreSelected bool `yaml:"store-selected"`
|
StoreSelected bool `yaml:"store-selected"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tun config
|
||||||
|
type Tun struct {
|
||||||
|
Enable bool `yaml:"enable" json:"enable"`
|
||||||
|
Stack string `yaml:"stack" json:"stack"`
|
||||||
|
DNSListen string `yaml:"dns-listen" json:"dns-listen"`
|
||||||
|
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
|
||||||
|
}
|
||||||
|
|
||||||
// Experimental config
|
// Experimental config
|
||||||
type Experimental struct{}
|
type Experimental struct{}
|
||||||
|
|
||||||
// Config is clash config manager
|
// Config is clash config manager
|
||||||
type Config struct {
|
type Config struct {
|
||||||
General *General
|
General *General
|
||||||
|
Tun *Tun
|
||||||
DNS *DNS
|
DNS *DNS
|
||||||
Experimental *Experimental
|
Experimental *Experimental
|
||||||
Hosts *trie.DomainTrie
|
Hosts *trie.DomainTrie
|
||||||
|
@ -137,6 +147,7 @@ type RawConfig struct {
|
||||||
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"`
|
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"`
|
||||||
Hosts map[string]string `yaml:"hosts"`
|
Hosts map[string]string `yaml:"hosts"`
|
||||||
DNS RawDNS `yaml:"dns"`
|
DNS RawDNS `yaml:"dns"`
|
||||||
|
Tun Tun `yaml:"tun"`
|
||||||
Experimental Experimental `yaml:"experimental"`
|
Experimental Experimental `yaml:"experimental"`
|
||||||
Profile Profile `yaml:"profile"`
|
Profile Profile `yaml:"profile"`
|
||||||
Proxy []map[string]interface{} `yaml:"proxies"`
|
Proxy []map[string]interface{} `yaml:"proxies"`
|
||||||
|
@ -166,6 +177,12 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
||||||
Rule: []string{},
|
Rule: []string{},
|
||||||
Proxy: []map[string]interface{}{},
|
Proxy: []map[string]interface{}{},
|
||||||
ProxyGroup: []map[string]interface{}{},
|
ProxyGroup: []map[string]interface{}{},
|
||||||
|
Tun: Tun{
|
||||||
|
Enable: false,
|
||||||
|
Stack: "system",
|
||||||
|
DNSListen: "0.0.0.0:53",
|
||||||
|
AutoRoute: true,
|
||||||
|
},
|
||||||
DNS: RawDNS{
|
DNS: RawDNS{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
UseHosts: true,
|
UseHosts: true,
|
||||||
|
@ -176,7 +193,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
||||||
},
|
},
|
||||||
DefaultNameserver: []string{
|
DefaultNameserver: []string{
|
||||||
"114.114.114.114",
|
"114.114.114.114",
|
||||||
"8.8.8.8",
|
"223.5.5.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Profile: Profile{
|
Profile: Profile{
|
||||||
|
@ -252,6 +269,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||||
RedirPort: cfg.RedirPort,
|
RedirPort: cfg.RedirPort,
|
||||||
TProxyPort: cfg.TProxyPort,
|
TProxyPort: cfg.TProxyPort,
|
||||||
MixedPort: cfg.MixedPort,
|
MixedPort: cfg.MixedPort,
|
||||||
|
Tun: cfg.Tun,
|
||||||
AllowLan: cfg.AllowLan,
|
AllowLan: cfg.AllowLan,
|
||||||
BindAddress: cfg.BindAddress,
|
BindAddress: cfg.BindAddress,
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,13 +6,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/mmdb"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func downloadMMDB(path string) (err error) {
|
func downloadGeoIP(path string) (err error) {
|
||||||
resp, err := http.Get("https://cdn.jsdelivr.net/gh/Dreamacro/maxmind-geoip@release/Country.mmdb")
|
resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -28,23 +27,42 @@ func downloadMMDB(path string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMMDB() error {
|
func downloadGeoSite(path string) (err error) {
|
||||||
if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {
|
resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat")
|
||||||
log.Infoln("Can't find MMDB, start download")
|
if err != nil {
|
||||||
if err := downloadMMDB(C.Path.MMDB()); err != nil {
|
return
|
||||||
return fmt.Errorf("can't download MMDB: %s", err.Error())
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(f, resp.Body)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func initGeoIP() error {
|
||||||
|
if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {
|
||||||
|
log.Infoln("Can't find GeoIP.dat, start download")
|
||||||
|
if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
log.Infoln("Download GeoIP.dat finish")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !mmdb.Verify() {
|
return nil
|
||||||
log.Warnln("MMDB invalid, remove and download")
|
}
|
||||||
if err := os.Remove(C.Path.MMDB()); err != nil {
|
|
||||||
return fmt.Errorf("can't remove invalid MMDB: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := downloadMMDB(C.Path.MMDB()); err != nil {
|
func initGeoSite() error {
|
||||||
return fmt.Errorf("can't download MMDB: %s", err.Error())
|
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
|
||||||
|
log.Infoln("Can't find GeoSite.dat, start download")
|
||||||
|
if err := downloadGeoSite(C.Path.GeoSite()); err != nil {
|
||||||
|
return fmt.Errorf("can't download GeoSite.dat: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
log.Infoln("Download GeoSite.dat finish")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -70,9 +88,14 @@ func Init(dir string) error {
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial mmdb
|
// initial GeoIP
|
||||||
if err := initMMDB(); err != nil {
|
if err := initGeoIP(); err != nil {
|
||||||
return fmt.Errorf("can't initial MMDB: %w", err)
|
return fmt.Errorf("can't initial GeoIP: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial GeoSite
|
||||||
|
if err := initGeoSite(); err != nil {
|
||||||
|
return fmt.Errorf("can't initial GeoSite: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ const (
|
||||||
Socks5
|
Socks5
|
||||||
Http
|
Http
|
||||||
Vmess
|
Vmess
|
||||||
|
Vless
|
||||||
Trojan
|
Trojan
|
||||||
|
|
||||||
Relay
|
Relay
|
||||||
|
@ -132,6 +133,8 @@ func (at AdapterType) String() string {
|
||||||
return "Http"
|
return "Http"
|
||||||
case Vmess:
|
case Vmess:
|
||||||
return "Vmess"
|
return "Vmess"
|
||||||
|
case Vless:
|
||||||
|
return "Vless"
|
||||||
case Trojan:
|
case Trojan:
|
||||||
return "Trojan"
|
return "Trojan"
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,14 @@ const (
|
||||||
|
|
||||||
TCP NetWork = iota
|
TCP NetWork = iota
|
||||||
UDP
|
UDP
|
||||||
|
ALLNet
|
||||||
|
|
||||||
HTTP Type = iota
|
HTTP Type = iota
|
||||||
HTTPCONNECT
|
HTTPCONNECT
|
||||||
SOCKS
|
SOCKS
|
||||||
REDIR
|
REDIR
|
||||||
TPROXY
|
TPROXY
|
||||||
|
TUN
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetWork int
|
type NetWork int
|
||||||
|
@ -27,8 +29,10 @@ type NetWork int
|
||||||
func (n NetWork) String() string {
|
func (n NetWork) String() string {
|
||||||
if n == TCP {
|
if n == TCP {
|
||||||
return "tcp"
|
return "tcp"
|
||||||
|
} else if n == UDP {
|
||||||
|
return "udp"
|
||||||
}
|
}
|
||||||
return "udp"
|
return "all"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NetWork) MarshalJSON() ([]byte, error) {
|
func (n NetWork) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -49,6 +53,8 @@ func (t Type) String() string {
|
||||||
return "Redir"
|
return "Redir"
|
||||||
case TPROXY:
|
case TPROXY:
|
||||||
return "TProxy"
|
return "TProxy"
|
||||||
|
case TUN:
|
||||||
|
return "Tun"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
@ -68,6 +74,7 @@ type Metadata struct {
|
||||||
DstPort string `json:"destinationPort"`
|
DstPort string `json:"destinationPort"`
|
||||||
AddrType int `json:"-"`
|
AddrType int `json:"-"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
|
Process string `json:"process"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metadata) RemoteAddress() string {
|
func (m *Metadata) RemoteAddress() string {
|
||||||
|
|
|
@ -60,3 +60,15 @@ func (p *path) MMDB() string {
|
||||||
func (p *path) Cache() string {
|
func (p *path) Cache() string {
|
||||||
return P.Join(p.homeDir, ".cache")
|
return P.Join(p.homeDir, ".cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *path) GeoIP() string {
|
||||||
|
return P.Join(p.homeDir, "geoip.dat")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) GeoSite() string {
|
||||||
|
return P.Join(p.homeDir, "geosite.dat")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) GetAssetLocation(file string) string {
|
||||||
|
return P.Join(p.homeDir, file)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ const (
|
||||||
Domain RuleType = iota
|
Domain RuleType = iota
|
||||||
DomainSuffix
|
DomainSuffix
|
||||||
DomainKeyword
|
DomainKeyword
|
||||||
|
GEOSITE
|
||||||
GEOIP
|
GEOIP
|
||||||
IPCIDR
|
IPCIDR
|
||||||
SrcIPCIDR
|
SrcIPCIDR
|
||||||
|
@ -24,6 +25,8 @@ func (rt RuleType) String() string {
|
||||||
return "DomainSuffix"
|
return "DomainSuffix"
|
||||||
case DomainKeyword:
|
case DomainKeyword:
|
||||||
return "DomainKeyword"
|
return "DomainKeyword"
|
||||||
|
case GEOSITE:
|
||||||
|
return "GeoSite"
|
||||||
case GEOIP:
|
case GEOIP:
|
||||||
return "GeoIP"
|
return "GeoIP"
|
||||||
case IPCIDR:
|
case IPCIDR:
|
||||||
|
@ -49,4 +52,5 @@ type Rule interface {
|
||||||
Adapter() string
|
Adapter() string
|
||||||
Payload() string
|
Payload() string
|
||||||
ShouldResolveIP() bool
|
ShouldResolveIP() bool
|
||||||
|
NetWork() NetWork
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,15 @@ package dns
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/mmdb"
|
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
_ "github.com/Dreamacro/clash/rule/geodata/standard"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var geoIPMatcher *router.GeoIPMatcher
|
||||||
|
|
||||||
type fallbackIPFilter interface {
|
type fallbackIPFilter interface {
|
||||||
Match(net.IP) bool
|
Match(net.IP) bool
|
||||||
}
|
}
|
||||||
|
@ -14,8 +19,35 @@ type fallbackIPFilter interface {
|
||||||
type geoipFilter struct{}
|
type geoipFilter struct{}
|
||||||
|
|
||||||
func (gf *geoipFilter) Match(ip net.IP) bool {
|
func (gf *geoipFilter) Match(ip net.IP) bool {
|
||||||
record, _ := mmdb.Instance().Country(ip)
|
if geoIPMatcher == nil {
|
||||||
return record.Country.IsoCode != "CN" && record.Country.IsoCode != ""
|
countryCode := "cn"
|
||||||
|
geoLoader, err := geodata.GetGeoDataLoader("standard")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("[GeoIPFilter] GetGeoDataLoader error: %s", err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := geoLoader.LoadGeoIP(countryCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
geoIP := &router.GeoIP{
|
||||||
|
CountryCode: countryCode,
|
||||||
|
Cidr: records,
|
||||||
|
ReverseMatch: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
geoIPMatcher, err = router.NewGeoIPMatcher(geoIP)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("[GeoIPFilter] NewGeoIPMatcher error: %s", err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !geoIPMatcher.Match(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipnetFilter struct {
|
type ipnetFilter struct {
|
||||||
|
|
|
@ -169,7 +169,7 @@ func compose(middlewares []middleware, endpoint handler) handler {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
|
func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
|
||||||
middlewares := []middleware{}
|
middlewares := []middleware{}
|
||||||
|
|
||||||
if resolver.hosts != nil {
|
if resolver.hosts != nil {
|
||||||
|
|
16
dns/patch.go
Normal file
16
dns/patch.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import D "github.com/miekg/dns"
|
||||||
|
|
||||||
|
type LocalServer struct {
|
||||||
|
handler handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeMsg implement resolver.LocalServer ResolveMsg
|
||||||
|
func (s *LocalServer) ServeMsg(msg *D.Msg) (*D.Msg, error) {
|
||||||
|
return handlerWithContext(s.handler, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer {
|
||||||
|
return &LocalServer{handler: NewHandler(resolver, mapper)}
|
||||||
|
}
|
|
@ -43,14 +43,14 @@ func handlerWithContext(handler handler, msg *D.Msg) (*D.Msg, error) {
|
||||||
return handler(ctx, msg)
|
return handler(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) setHandler(handler handler) {
|
func (s *Server) SetHandler(handler handler) {
|
||||||
s.handler = handler
|
s.handler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) error {
|
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) error {
|
||||||
if addr == address && resolver != nil {
|
if addr == address && resolver != nil {
|
||||||
handler := newHandler(resolver, mapper)
|
handler := NewHandler(resolver, mapper)
|
||||||
server.setHandler(handler)
|
server.SetHandler(handler)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) e
|
||||||
}
|
}
|
||||||
|
|
||||||
address = addr
|
address = addr
|
||||||
handler := newHandler(resolver, mapper)
|
handler := NewHandler(resolver, mapper)
|
||||||
server = &Server{handler: handler}
|
server = &Server{handler: handler}
|
||||||
server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server}
|
server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server}
|
||||||
|
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -9,8 +9,8 @@ require (
|
||||||
github.com/go-chi/render v1.0.1
|
github.com/go-chi/render v1.0.1
|
||||||
github.com/gofrs/uuid v4.0.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/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99
|
||||||
github.com/miekg/dns v1.1.42
|
github.com/miekg/dns v1.1.42
|
||||||
github.com/oschwald/geoip2-golang v1.5.0
|
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
go.uber.org/atomic v1.7.0
|
go.uber.org/atomic v1.7.0
|
||||||
|
@ -18,5 +18,7 @@ require (
|
||||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701
|
golang.org/x/net v0.0.0-20210508051633-16afe75a6701
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096
|
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096
|
||||||
|
google.golang.org/protobuf v1.26.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
gvisor.dev/gvisor v0.0.0-20210519191755-bd7eb2c99ba9
|
||||||
)
|
)
|
||||||
|
|
591
go.sum
591
go.sum
|
@ -1,59 +1,648 @@
|
||||||
|
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
|
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
|
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||||
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
|
||||||
|
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||||
|
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||||
|
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||||
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||||
|
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/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 v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||||
|
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
|
github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
|
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
|
||||||
|
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||||
|
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||||
|
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||||
|
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
|
||||||
|
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||||
|
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||||
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||||
|
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||||
|
github.com/coreos/go-systemd/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/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||||
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-chi/chi/v5 v5.0.3 h1:khYQBdPivkYG1s1TAzDQG1f6eX4kD2TItYVZexL5rS4=
|
github.com/go-chi/chi/v5 v5.0.3 h1:khYQBdPivkYG1s1TAzDQG1f6eX4kD2TItYVZexL5rS4=
|
||||||
github.com/go-chi/chi/v5 v5.0.3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
|
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
|
||||||
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
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-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
|
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||||
|
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||||
|
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||||
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210115211752-39141e76b647/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
|
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
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/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 h1:dkEFEnGUg2z/FAPywWr4yfR/sWDQK76qn3J4Y5H2hJs=
|
||||||
|
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||||
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
||||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.6.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 v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
|
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||||
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
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/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
|
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5/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/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
go.uber.org/atomic v1.7.0 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/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701 h1:lQVgcB3+FoAXOb20Dp6zTzAIrpj1k/yOOBN7s+Zv1rA=
|
golang.org/x/net v0.0.0-20210508051633-16afe75a6701 h1:lQVgcB3+FoAXOb20Dp6zTzAIrpj1k/yOOBN7s+Zv1rA=
|
||||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210508051633-16afe75a6701/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo=
|
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo=
|
||||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/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/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=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-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-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.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.20210208035533-9280052d3665/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20210519191755-bd7eb2c99ba9 h1:7Xn0JTQiWLxAKGI5FCtQH4DVnQ4K7tBZ6hVSgitTZH8=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20210519191755-bd7eb2c99ba9/go.mod h1:ucHEMlckp+S/YzKEpwwAyGBhAh807Wxq/8Erc6gFxCE=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||||
|
k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs=
|
||||||
|
k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
|
||||||
|
k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
|
||||||
|
k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M=
|
||||||
|
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
|
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||||
|
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||||
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||||
|
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
|
|
@ -3,7 +3,11 @@ package executor
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter"
|
"github.com/Dreamacro/clash/adapter"
|
||||||
|
@ -20,6 +24,8 @@ import (
|
||||||
"github.com/Dreamacro/clash/dns"
|
"github.com/Dreamacro/clash/dns"
|
||||||
P "github.com/Dreamacro/clash/listener"
|
P "github.com/Dreamacro/clash/listener"
|
||||||
authStore "github.com/Dreamacro/clash/listener/auth"
|
authStore "github.com/Dreamacro/clash/listener/auth"
|
||||||
|
"github.com/Dreamacro/clash/listener/tproxy"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
)
|
)
|
||||||
|
@ -70,13 +76,16 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
||||||
defer mux.Unlock()
|
defer mux.Unlock()
|
||||||
|
|
||||||
updateUsers(cfg.Users)
|
updateUsers(cfg.Users)
|
||||||
|
updateDNS(cfg.DNS, cfg.General)
|
||||||
updateGeneral(cfg.General, force)
|
updateGeneral(cfg.General, force)
|
||||||
|
log.SetLevel(log.DEBUG)
|
||||||
updateProxies(cfg.Proxies, cfg.Providers)
|
updateProxies(cfg.Proxies, cfg.Providers)
|
||||||
updateRules(cfg.Rules)
|
updateRules(cfg.Rules)
|
||||||
updateDNS(cfg.DNS)
|
|
||||||
updateHosts(cfg.Hosts)
|
updateHosts(cfg.Hosts)
|
||||||
updateExperimental(cfg)
|
updateExperimental(cfg)
|
||||||
updateProfile(cfg)
|
updateProfile(cfg)
|
||||||
|
updateIPTables(cfg.DNS, cfg.General)
|
||||||
|
log.SetLevel(cfg.General.LogLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGeneral() *config.General {
|
func GetGeneral() *config.General {
|
||||||
|
@ -93,6 +102,7 @@ func GetGeneral() *config.General {
|
||||||
RedirPort: ports.RedirPort,
|
RedirPort: ports.RedirPort,
|
||||||
TProxyPort: ports.TProxyPort,
|
TProxyPort: ports.TProxyPort,
|
||||||
MixedPort: ports.MixedPort,
|
MixedPort: ports.MixedPort,
|
||||||
|
Tun: P.Tun(),
|
||||||
Authentication: authenticator,
|
Authentication: authenticator,
|
||||||
AllowLan: P.AllowLan(),
|
AllowLan: P.AllowLan(),
|
||||||
BindAddress: P.BindAddress(),
|
BindAddress: P.BindAddress(),
|
||||||
|
@ -107,7 +117,7 @@ func GetGeneral() *config.General {
|
||||||
|
|
||||||
func updateExperimental(c *config.Config) {}
|
func updateExperimental(c *config.Config) {}
|
||||||
|
|
||||||
func updateDNS(c *config.DNS) {
|
func updateDNS(c *config.DNS, general *config.General) {
|
||||||
if !c.Enable {
|
if !c.Enable {
|
||||||
resolver.DefaultResolver = nil
|
resolver.DefaultResolver = nil
|
||||||
resolver.DefaultHostMapper = nil
|
resolver.DefaultHostMapper = nil
|
||||||
|
@ -141,6 +151,9 @@ func updateDNS(c *config.DNS) {
|
||||||
|
|
||||||
resolver.DefaultResolver = r
|
resolver.DefaultResolver = r
|
||||||
resolver.DefaultHostMapper = m
|
resolver.DefaultHostMapper = m
|
||||||
|
if general.Tun.Enable && strings.EqualFold(general.Tun.Stack, "system") {
|
||||||
|
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
||||||
|
}
|
||||||
|
|
||||||
if err := dns.ReCreateServer(c.Listen, r, m); err != nil {
|
if err := dns.ReCreateServer(c.Listen, r, m); err != nil {
|
||||||
log.Errorln("Start DNS server error: %s", err.Error())
|
log.Errorln("Start DNS server error: %s", err.Error())
|
||||||
|
@ -165,10 +178,24 @@ func updateRules(rules []C.Rule) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateGeneral(general *config.General, force bool) {
|
func updateGeneral(general *config.General, force bool) {
|
||||||
log.SetLevel(general.LogLevel)
|
log.SetLevel(log.DEBUG)
|
||||||
tunnel.SetMode(general.Mode)
|
tunnel.SetMode(general.Mode)
|
||||||
resolver.DisableIPv6 = !general.IPv6
|
resolver.DisableIPv6 = !general.IPv6
|
||||||
|
|
||||||
|
if (general.Tun.Enable || general.TProxyPort != 0) && general.Interface == "" {
|
||||||
|
autoDetectInterfaceName, err := dev.GetAutoDetectInterface()
|
||||||
|
if err == nil {
|
||||||
|
if autoDetectInterfaceName != "" && autoDetectInterfaceName != "<nil>" {
|
||||||
|
general.Interface = autoDetectInterfaceName
|
||||||
|
log.Infoln("Use auto detect interface: %s", general.Interface)
|
||||||
|
} else {
|
||||||
|
log.Debugln("Auto detect interface is empty.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debugln("Can not find auto detect interface. %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if general.Interface != "" {
|
if general.Interface != "" {
|
||||||
dialer.DialHook = dialer.DialerWithInterface(general.Interface)
|
dialer.DialHook = dialer.DialerWithInterface(general.Interface)
|
||||||
dialer.ListenPacketHook = dialer.ListenPacketWithInterface(general.Interface)
|
dialer.ListenPacketHook = dialer.ListenPacketWithInterface(general.Interface)
|
||||||
|
@ -178,6 +205,7 @@ func updateGeneral(general *config.General, force bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !force {
|
if !force {
|
||||||
|
log.SetLevel(general.LogLevel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +237,13 @@ func updateGeneral(general *config.General, force bool) {
|
||||||
if err := P.ReCreateMixed(general.MixedPort, tcpIn, udpIn); err != nil {
|
if err := P.ReCreateMixed(general.MixedPort, tcpIn, udpIn); err != nil {
|
||||||
log.Errorln("Start Mixed(http and socks5) server error: %s", err.Error())
|
log.Errorln("Start Mixed(http and socks5) server error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := P.ReCreateTun(general.Tun, tcpIn, udpIn); err != nil {
|
||||||
|
log.Errorln("Start Tun interface error: %s", err.Error())
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetLevel(general.LogLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUsers(users []auth.AuthUser) {
|
func updateUsers(users []auth.AuthUser) {
|
||||||
|
@ -253,3 +288,34 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
|
||||||
selector.Set(selected)
|
selector.Set(selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateIPTables(dns *config.DNS, general *config.General) {
|
||||||
|
if runtime.GOOS != "linux" || dns.Listen == "" || general.TProxyPort == 0 || general.Tun.Enable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, dnsPortStr, err := net.SplitHostPort(dns.Listen)
|
||||||
|
if dnsPortStr == "0" || dnsPortStr == "" || err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsPort, err := strconv.Atoi(dnsPortStr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tproxy.SetTProxyLinuxIPTables(general.Interface, general.TProxyPort, dnsPort)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Can not setting iptables for TProxy on linux, %s", err.Error())
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CleanUp() {
|
||||||
|
P.CleanUp()
|
||||||
|
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
tproxy.CleanUpTProxyLinuxIPTables()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,3 +48,7 @@ func Parse(options ...Option) error {
|
||||||
executor.ApplyConfig(cfg, true)
|
executor.ApplyConfig(cfg, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanUp() {
|
||||||
|
executor.CleanUp()
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ type configSchema struct {
|
||||||
RedirPort *int `json:"redir-port"`
|
RedirPort *int `json:"redir-port"`
|
||||||
TProxyPort *int `json:"tproxy-port"`
|
TProxyPort *int `json:"tproxy-port"`
|
||||||
MixedPort *int `json:"mixed-port"`
|
MixedPort *int `json:"mixed-port"`
|
||||||
|
Tun *config.Tun `json:"tun"`
|
||||||
AllowLan *bool `json:"allow-lan"`
|
AllowLan *bool `json:"allow-lan"`
|
||||||
BindAddress *string `json:"bind-address"`
|
BindAddress *string `json:"bind-address"`
|
||||||
Mode *tunnel.TunnelMode `json:"mode"`
|
Mode *tunnel.TunnelMode `json:"mode"`
|
||||||
|
@ -77,6 +78,18 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||||
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn)
|
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn)
|
||||||
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
|
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
|
||||||
|
|
||||||
|
if general.Tun != nil {
|
||||||
|
err := P.ReCreateTun(*general.Tun, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
log.Infoln("Recreate tun success.")
|
||||||
|
} else {
|
||||||
|
log.Errorln("Recreate tun failed: %s", err.Error())
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, newError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if general.Mode != nil {
|
if general.Mode != nil {
|
||||||
tunnel.SetMode(*general.Mode)
|
tunnel.SetMode(*general.Mode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,20 @@ package proxy
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/config"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/listener/http"
|
"github.com/Dreamacro/clash/listener/http"
|
||||||
"github.com/Dreamacro/clash/listener/mixed"
|
"github.com/Dreamacro/clash/listener/mixed"
|
||||||
"github.com/Dreamacro/clash/listener/redir"
|
"github.com/Dreamacro/clash/listener/redir"
|
||||||
"github.com/Dreamacro/clash/listener/socks"
|
"github.com/Dreamacro/clash/listener/socks"
|
||||||
"github.com/Dreamacro/clash/listener/tproxy"
|
"github.com/Dreamacro/clash/listener/tproxy"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +33,7 @@ var (
|
||||||
tproxyUDPListener *tproxy.UDPListener
|
tproxyUDPListener *tproxy.UDPListener
|
||||||
mixedListener *mixed.Listener
|
mixedListener *mixed.Listener
|
||||||
mixedUDPLister *socks.UDPListener
|
mixedUDPLister *socks.UDPListener
|
||||||
|
tunAdapter ipstack.TunAdapter
|
||||||
|
|
||||||
// lock for recreate function
|
// lock for recreate function
|
||||||
socksMux sync.Mutex
|
socksMux sync.Mutex
|
||||||
|
@ -36,6 +41,7 @@ var (
|
||||||
redirMux sync.Mutex
|
redirMux sync.Mutex
|
||||||
tproxyMux sync.Mutex
|
tproxyMux sync.Mutex
|
||||||
mixedMux sync.Mutex
|
mixedMux sync.Mutex
|
||||||
|
tunMux sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ports struct {
|
type Ports struct {
|
||||||
|
@ -58,6 +64,18 @@ func SetAllowLan(al bool) {
|
||||||
allowLan = al
|
allowLan = al
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Tun() config.Tun {
|
||||||
|
if tunAdapter == nil {
|
||||||
|
return config.Tun{}
|
||||||
|
}
|
||||||
|
return config.Tun{
|
||||||
|
Enable: true,
|
||||||
|
Stack: tunAdapter.Stack(),
|
||||||
|
DNSListen: tunAdapter.DNSListen(),
|
||||||
|
AutoRoute: tunAdapter.AutoRoute(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetBindAddress(host string) {
|
func SetBindAddress(host string) {
|
||||||
bindAddress = host
|
bindAddress = host
|
||||||
}
|
}
|
||||||
|
@ -275,6 +293,25 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
|
||||||
|
tunMux.Lock()
|
||||||
|
defer tunMux.Unlock()
|
||||||
|
|
||||||
|
if tunAdapter != nil {
|
||||||
|
tunAdapter.Close()
|
||||||
|
tunAdapter = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conf.Enable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
tunAdapter, err = tun.New(conf, tcpIn, udpIn)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// GetPorts return the ports of proxy servers
|
// GetPorts return the ports of proxy servers
|
||||||
func GetPorts() *Ports {
|
func GetPorts() *Ports {
|
||||||
ports := &Ports{}
|
ports := &Ports{}
|
||||||
|
@ -330,3 +367,12 @@ func genAddr(host string, port int, allowLan bool) string {
|
||||||
|
|
||||||
return fmt.Sprintf("127.0.0.1:%d", port)
|
return fmt.Sprintf("127.0.0.1:%d", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CleanUp clean up something
|
||||||
|
func CleanUp() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if tunAdapter != nil {
|
||||||
|
tunAdapter.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
192
listener/tproxy/tproxy_linux_iptables.go
Normal file
192
listener/tproxy/tproxy_linux_iptables.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package tproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
U "os/user"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
interfaceName = ""
|
||||||
|
tproxyPort = 0
|
||||||
|
dnsPort = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROXY_FWMARK = "0x2d0"
|
||||||
|
PROXY_ROUTE_TABLE = "0x2d0"
|
||||||
|
USERNAME = "clash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
|
||||||
|
var err error
|
||||||
|
if _, err = execCmd("iptables -V"); err != nil {
|
||||||
|
return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
//if _, err = execCmd("modprobe xt_TPROXY"); err != nil {
|
||||||
|
// return errors.New("xt_TPROXY module does not exist, please install it")
|
||||||
|
//}
|
||||||
|
|
||||||
|
user, err := U.Lookup(USERNAME)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("the user \" %s\" does not exist, please create it", USERNAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifname == "" {
|
||||||
|
return errors.New("interface name can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerUid := user.Uid
|
||||||
|
|
||||||
|
interfaceName = ifname
|
||||||
|
tproxyPort = tport
|
||||||
|
dnsPort = dport
|
||||||
|
|
||||||
|
// add route
|
||||||
|
execCmd(fmt.Sprintf("ip -f inet rule add fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
|
||||||
|
execCmd(fmt.Sprintf("ip -f inet route add local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
|
||||||
|
|
||||||
|
// set FORWARD
|
||||||
|
execCmd("sysctl -w net.ipv4.ip_forward=1")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
|
||||||
|
|
||||||
|
// set clash divert
|
||||||
|
execCmd("iptables -t mangle -N clash_divert")
|
||||||
|
execCmd("iptables -t mangle -F clash_divert")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_divert -j MARK --set-mark %s", PROXY_FWMARK))
|
||||||
|
execCmd("iptables -t mangle -A clash_divert -j ACCEPT")
|
||||||
|
|
||||||
|
// set pre routing
|
||||||
|
execCmd("iptables -t mangle -N clash_prerouting")
|
||||||
|
execCmd("iptables -t mangle -F clash_prerouting")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -s 172.17.0.0/16 -j RETURN")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -p udp --dport 53 -j ACCEPT")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -p tcp --dport 53 -j ACCEPT")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -m addrtype --dst-type LOCAL -j RETURN")
|
||||||
|
addLocalnetworkToChain("clash_prerouting")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -p tcp -m socket -j clash_divert")
|
||||||
|
execCmd("iptables -t mangle -A clash_prerouting -p udp -m socket -j clash_divert")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK))
|
||||||
|
execCmd("iptables -t mangle -A PREROUTING -j clash_prerouting")
|
||||||
|
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort))
|
||||||
|
|
||||||
|
// set post routing
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
|
||||||
|
|
||||||
|
// set output
|
||||||
|
execCmd("iptables -t mangle -N clash_output")
|
||||||
|
execCmd("iptables -t mangle -F clash_output")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -m owner --uid-owner %s -j RETURN", ownerUid))
|
||||||
|
execCmd("iptables -t mangle -A clash_output -p udp -m multiport --dports 53,123,137 -j ACCEPT")
|
||||||
|
execCmd("iptables -t mangle -A clash_output -p tcp --dport 53 -j ACCEPT")
|
||||||
|
execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type LOCAL -j RETURN")
|
||||||
|
execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type BROADCAST -j RETURN")
|
||||||
|
addLocalnetworkToChain("clash_output")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p tcp -j MARK --set-mark %s", PROXY_FWMARK))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p udp -j MARK --set-mark %s", PROXY_FWMARK))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j clash_output", interfaceName))
|
||||||
|
|
||||||
|
// set dns output
|
||||||
|
execCmd("iptables -t nat -N clash_dns_output")
|
||||||
|
execCmd("iptables -t nat -F clash_dns_output")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -m owner --uid-owner %s -j RETURN", ownerUid))
|
||||||
|
execCmd("iptables -t nat -A clash_dns_output -s 172.17.0.0/16 -j RETURN")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort))
|
||||||
|
execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output")
|
||||||
|
execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output")
|
||||||
|
|
||||||
|
log.Infoln("[TProxy] Setting iptables completed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CleanUpTProxyLinuxIPTables() {
|
||||||
|
|
||||||
|
if interfaceName == "" || tproxyPort == 0 || dnsPort == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnln("Clean up tproxy linux iptables")
|
||||||
|
|
||||||
|
if _, err := execCmd("iptables -t mangle -L clash_divert"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean route
|
||||||
|
execCmd(fmt.Sprintf("ip -f inet rule del fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
|
||||||
|
execCmd(fmt.Sprintf("ip -f inet route del local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
|
||||||
|
|
||||||
|
// clean FORWARD
|
||||||
|
//execCmd("sysctl -w net.ipv4.ip_forward=0")
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName))
|
||||||
|
|
||||||
|
// clean PREROUTING
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort))
|
||||||
|
execCmd("iptables -t mangle -D PREROUTING -j clash_prerouting")
|
||||||
|
|
||||||
|
// clean POSTROUTING
|
||||||
|
execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
|
||||||
|
|
||||||
|
// clean OUTPUT
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j clash_output", interfaceName))
|
||||||
|
execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j clash_dns_output")
|
||||||
|
execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j clash_dns_output")
|
||||||
|
|
||||||
|
// clean chain
|
||||||
|
execCmd("iptables -t mangle -F clash_prerouting")
|
||||||
|
execCmd("iptables -t mangle -X clash_prerouting")
|
||||||
|
execCmd("iptables -t mangle -F clash_divert")
|
||||||
|
execCmd("iptables -t mangle -X clash_divert")
|
||||||
|
execCmd("iptables -t mangle -F clash_output")
|
||||||
|
execCmd("iptables -t mangle -X clash_output")
|
||||||
|
execCmd("iptables -t nat -F clash_dns_output")
|
||||||
|
execCmd("iptables -t nat -X clash_dns_output")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLocalnetworkToChain(chain string) {
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 0.0.0.0/8 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 10.0.0.0/8 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 100.64.0.0/10 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 127.0.0.0/8 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 169.254.0.0/16 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 172.16.0.0/12 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.0.0/24 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.2.0/24 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.88.99.0/24 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.168.0.0/16 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 198.51.100.0/24 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 203.0.113.0/24 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 224.0.0.0/4 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 240.0.0.0/4 -j RETURN", chain))
|
||||||
|
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain))
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCmd(cmdstr string) (string, error) {
|
||||||
|
log.Debugln("[TProxy] %s", cmdstr)
|
||||||
|
|
||||||
|
args := strings.Split(cmdstr, " ")
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("[TProxy] error: %s, %s", err.Error(), string(out))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out), nil
|
||||||
|
}
|
66
listener/tun/dev/dev.go
Normal file
66
listener/tun/dev/dev.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package dev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TunDevice is cross-platform tun interface
|
||||||
|
type TunDevice interface {
|
||||||
|
Name() string
|
||||||
|
URL() string
|
||||||
|
MTU() (int, error)
|
||||||
|
IsClose() bool
|
||||||
|
Close() error
|
||||||
|
Read(buff []byte) (int, error)
|
||||||
|
Write(buff []byte) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLinuxAutoRoute() {
|
||||||
|
log.Infoln("Tun adapter auto setting MacOS route")
|
||||||
|
addLinuxSystemRoute("1")
|
||||||
|
addLinuxSystemRoute("2/7")
|
||||||
|
addLinuxSystemRoute("4/6")
|
||||||
|
addLinuxSystemRoute("8/5")
|
||||||
|
addLinuxSystemRoute("16/4")
|
||||||
|
addLinuxSystemRoute("32/3")
|
||||||
|
addLinuxSystemRoute("64/2")
|
||||||
|
addLinuxSystemRoute("128.0/1")
|
||||||
|
addLinuxSystemRoute("198.18.0/16")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveLinuxAutoRoute() {
|
||||||
|
log.Infoln("Tun adapter removing MacOS route")
|
||||||
|
delLinuxSystemRoute("1")
|
||||||
|
delLinuxSystemRoute("2/7")
|
||||||
|
delLinuxSystemRoute("4/6")
|
||||||
|
delLinuxSystemRoute("8/5")
|
||||||
|
delLinuxSystemRoute("16/4")
|
||||||
|
delLinuxSystemRoute("32/3")
|
||||||
|
delLinuxSystemRoute("64/2")
|
||||||
|
delLinuxSystemRoute("128.0/1")
|
||||||
|
delLinuxSystemRoute("198.18.0/16")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLinuxSystemRoute(net string) {
|
||||||
|
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd := exec.Command("route", "add", "-net", net, "198.18.0.1")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Errorln("[MacOS auto route] Failed to add system route: %s, cmd: %s", err.Error(), cmd.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func delLinuxSystemRoute(net string) {
|
||||||
|
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd := exec.Command("route", "delete", "-net", net, "198.18.0.1")
|
||||||
|
_ = cmd.Run()
|
||||||
|
//if err := cmd.Run(); err != nil {
|
||||||
|
// log.Errorln("[MacOS auto route]Failed to delete system route: %s, cmd: %s", err.Error(), cmd.String())
|
||||||
|
//}
|
||||||
|
}
|
506
listener/tun/dev/dev_darwin.go
Normal file
506
listener/tun/dev/dev_darwin.go
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package dev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
|
)
|
||||||
|
|
||||||
|
const utunControlName = "com.apple.net.utun_control"
|
||||||
|
const _IOC_OUT = 0x40000000
|
||||||
|
const _IOC_IN = 0x80000000
|
||||||
|
const _IOC_INOUT = _IOC_IN | _IOC_OUT
|
||||||
|
|
||||||
|
// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ioccom.h
|
||||||
|
|
||||||
|
// #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ = 0xc0644e03
|
||||||
|
const _CTLIOCGINFO = _IOC_INOUT | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
|
||||||
|
|
||||||
|
// #define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) = 0x8080691a
|
||||||
|
//const _SIOCAIFADDR_IN6 = _IOC_IN | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 26
|
||||||
|
|
||||||
|
// #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq_64)
|
||||||
|
const _SIOCPROTOATTACH_IN6 = _IOC_INOUT | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 110
|
||||||
|
|
||||||
|
// #define SIOCLL_START _IOWR('i', 130, struct in6_aliasreq)
|
||||||
|
const _SIOCLL_START = _IOC_INOUT | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 130
|
||||||
|
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/nd6.h#L469
|
||||||
|
const ND6_INFINITE_LIFETIME = 0xffffffff
|
||||||
|
|
||||||
|
// Following the wireguard-go solution:
|
||||||
|
// These unix.SYS_* constants were removed from golang.org/x/sys/unix
|
||||||
|
// so copy them here for now.
|
||||||
|
// See https://github.com/golang/go/issues/41868
|
||||||
|
const (
|
||||||
|
sys_IOCTL = 54
|
||||||
|
sys_CONNECT = 98
|
||||||
|
sys_GETSOCKOPT = 118
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunDarwin struct {
|
||||||
|
//url string
|
||||||
|
name string
|
||||||
|
tunAddress string
|
||||||
|
autoRoute bool
|
||||||
|
tunFile *os.File
|
||||||
|
errors chan error
|
||||||
|
|
||||||
|
closed bool
|
||||||
|
stopOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h
|
||||||
|
type sockaddrCtl struct {
|
||||||
|
scLen uint8
|
||||||
|
scFamily uint8
|
||||||
|
ssSysaddr uint16
|
||||||
|
scID uint32
|
||||||
|
scUnit uint32
|
||||||
|
scReserved [5]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L402-L563
|
||||||
|
|
||||||
|
//type ifreqAddr struct {
|
||||||
|
// Name [unix.IFNAMSIZ]byte
|
||||||
|
// Addr unix.RawSockaddrInet4
|
||||||
|
// Pad [8]byte
|
||||||
|
//}
|
||||||
|
|
||||||
|
var sockaddrCtlSize uintptr = 32
|
||||||
|
|
||||||
|
// OpenTunDevice return a TunDevice according a URL
|
||||||
|
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
|
||||||
|
name := "utun"
|
||||||
|
// TODO: configure the MTU
|
||||||
|
mtu := 9000
|
||||||
|
|
||||||
|
ifIndex := -1
|
||||||
|
if name != "utun" {
|
||||||
|
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
|
||||||
|
if err != nil || ifIndex < 0 {
|
||||||
|
return nil, fmt.Errorf("interface name must be utun[0-9]*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctlInfo = &struct {
|
||||||
|
ctlID uint32
|
||||||
|
ctlName [96]byte
|
||||||
|
}{}
|
||||||
|
|
||||||
|
copy(ctlInfo.ctlName[:], []byte(utunControlName))
|
||||||
|
|
||||||
|
_, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(_CTLIOCGINFO),
|
||||||
|
uintptr(unsafe.Pointer(ctlInfo)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := sockaddrCtl{
|
||||||
|
scLen: uint8(sockaddrCtlSize),
|
||||||
|
scFamily: unix.AF_SYSTEM,
|
||||||
|
ssSysaddr: 2,
|
||||||
|
scID: ctlInfo.ctlID,
|
||||||
|
scUnit: uint32(ifIndex) + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
scPointer := unsafe.Pointer(&sc)
|
||||||
|
|
||||||
|
_, _, errno = unix.RawSyscall(
|
||||||
|
sys_CONNECT,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(scPointer),
|
||||||
|
uintptr(sockaddrCtlSize),
|
||||||
|
)
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = syscall.SetNonblock(fd, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, tunAddress, autoRoute)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if autoRoute {
|
||||||
|
SetLinuxAutoRoute()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tun, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateTUNFromFile(file *os.File, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
tun := &tunDarwin{
|
||||||
|
tunFile: file,
|
||||||
|
tunAddress: tunAddress,
|
||||||
|
autoRoute: autoRoute,
|
||||||
|
errors: make(chan error, 5),
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := tun.getName()
|
||||||
|
if err != nil {
|
||||||
|
tun.tunFile.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tun.name = name
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tun.tunFile.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mtu > 0 {
|
||||||
|
err = tun.setMTU(mtu)
|
||||||
|
if err != nil {
|
||||||
|
tun.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This address doesn't mean anything here. NIC just net an IP address to set route upon.
|
||||||
|
// TODO: maybe let user config it. And I'm doubt whether we really need it.
|
||||||
|
p2pAddress := net.ParseIP("198.18.0.1")
|
||||||
|
err = tun.setTunAddress(p2pAddress)
|
||||||
|
if err != nil {
|
||||||
|
tun.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = tun.attachLinkLocal()
|
||||||
|
if err != nil {
|
||||||
|
tun.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tun, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) Name() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) URL() string {
|
||||||
|
return fmt.Sprintf("dev://%s", t.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) MTU() (int, error) {
|
||||||
|
return t.getInterfaceMtu()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) Read(buff []byte) (int, error) {
|
||||||
|
select {
|
||||||
|
case err := <-t.errors:
|
||||||
|
return 0, err
|
||||||
|
default:
|
||||||
|
n, err := t.tunFile.Read(buff)
|
||||||
|
if n < 4 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(buff[:], buff[4:])
|
||||||
|
return n - 4, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) Write(buff []byte) (int, error) {
|
||||||
|
// reserve space for header
|
||||||
|
buf := pool.Get(pool.RelayBufferSize)
|
||||||
|
defer pool.Put(buf[:cap(buf)])
|
||||||
|
|
||||||
|
buf[0] = 0x00
|
||||||
|
buf[1] = 0x00
|
||||||
|
buf[2] = 0x00
|
||||||
|
|
||||||
|
copy(buf[4:], buff)
|
||||||
|
if buf[4]>>4 == ipv6.Version {
|
||||||
|
buf[3] = unix.AF_INET6
|
||||||
|
} else {
|
||||||
|
buf[3] = unix.AF_INET
|
||||||
|
}
|
||||||
|
|
||||||
|
// write
|
||||||
|
return t.tunFile.Write(buf[:4+len(buff)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) IsClose() bool {
|
||||||
|
return t.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) Close() error {
|
||||||
|
t.stopOnce.Do(func() {
|
||||||
|
if t.autoRoute {
|
||||||
|
RemoveLinuxAutoRoute()
|
||||||
|
}
|
||||||
|
t.closed = true
|
||||||
|
t.tunFile.Close()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) getInterfaceMtu() (int, error) {
|
||||||
|
|
||||||
|
// open datagram socket
|
||||||
|
|
||||||
|
fd, err := unix.Socket(
|
||||||
|
unix.AF_INET,
|
||||||
|
unix.SOCK_DGRAM,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer unix.Close(fd)
|
||||||
|
|
||||||
|
// do ioctl call
|
||||||
|
|
||||||
|
var ifr [64]byte
|
||||||
|
copy(ifr[:], t.name)
|
||||||
|
_, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(unix.SIOCGIFMTU),
|
||||||
|
uintptr(unsafe.Pointer(&ifr[0])),
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, fmt.Errorf("failed to get MTU on %s", t.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) getName() (string, error) {
|
||||||
|
var ifName struct {
|
||||||
|
name [16]byte
|
||||||
|
}
|
||||||
|
ifNameSize := uintptr(16)
|
||||||
|
|
||||||
|
var errno syscall.Errno
|
||||||
|
t.operateOnFd(func(fd uintptr) {
|
||||||
|
_, _, errno = unix.Syscall6(
|
||||||
|
sys_GETSOCKOPT,
|
||||||
|
fd,
|
||||||
|
2, /* #define SYSPROTO_CONTROL 2 */
|
||||||
|
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||||
|
uintptr(unsafe.Pointer(&ifName)),
|
||||||
|
uintptr(unsafe.Pointer(&ifNameSize)), 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.name = string(ifName.name[:ifNameSize-1])
|
||||||
|
return t.name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) setMTU(n int) error {
|
||||||
|
// open datagram socket
|
||||||
|
fd, err := unix.Socket(
|
||||||
|
unix.AF_INET,
|
||||||
|
unix.SOCK_DGRAM,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer unix.Close(fd)
|
||||||
|
|
||||||
|
// do ioctl call
|
||||||
|
|
||||||
|
var ifr [32]byte
|
||||||
|
copy(ifr[:], t.name)
|
||||||
|
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
|
||||||
|
_, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(unix.SIOCSIFMTU),
|
||||||
|
uintptr(unsafe.Pointer(&ifr[0])),
|
||||||
|
)
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return fmt.Errorf("failed to set MTU on %s", t.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) operateOnFd(fn func(fd uintptr)) {
|
||||||
|
sysconn, err := t.tunFile.SyscallConn()
|
||||||
|
// TODO: consume the errors
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = sysconn.Control(fn)
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) setTunAddress(addr net.IP) error {
|
||||||
|
var ifr [unix.IFNAMSIZ]byte
|
||||||
|
copy(ifr[:], t.name)
|
||||||
|
|
||||||
|
// set IPv4 address
|
||||||
|
fd4, err := unix.Socket(
|
||||||
|
unix.AF_INET,
|
||||||
|
unix.SOCK_DGRAM,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer syscall.Close(fd4)
|
||||||
|
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/sockio.h#L107
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L570-L575
|
||||||
|
// https://man.openbsd.org/netintro.4#SIOCAIFADDR
|
||||||
|
type aliasreq struct {
|
||||||
|
ifra_name [unix.IFNAMSIZ]byte
|
||||||
|
ifra_addr unix.RawSockaddrInet4
|
||||||
|
ifra_dstaddr unix.RawSockaddrInet4
|
||||||
|
ifra_mask unix.RawSockaddrInet4
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip4 [4]byte
|
||||||
|
copy(ip4[:], addr.To4())
|
||||||
|
ip4mask := [4]byte{255, 255, 0, 0}
|
||||||
|
ifra4 := aliasreq{
|
||||||
|
ifra_name: ifr,
|
||||||
|
ifra_addr: unix.RawSockaddrInet4{
|
||||||
|
Len: unix.SizeofSockaddrInet4,
|
||||||
|
Family: unix.AF_INET,
|
||||||
|
Addr: ip4,
|
||||||
|
},
|
||||||
|
ifra_dstaddr: unix.RawSockaddrInet4{
|
||||||
|
Len: unix.SizeofSockaddrInet4,
|
||||||
|
Family: unix.AF_INET,
|
||||||
|
Addr: ip4,
|
||||||
|
},
|
||||||
|
ifra_mask: unix.RawSockaddrInet4{
|
||||||
|
Len: unix.SizeofSockaddrInet4,
|
||||||
|
Family: unix.AF_INET,
|
||||||
|
Addr: ip4mask,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd4),
|
||||||
|
uintptr(unix.SIOCAIFADDR),
|
||||||
|
uintptr(unsafe.Pointer(&ifra4)),
|
||||||
|
); errno != 0 {
|
||||||
|
return fmt.Errorf("failed to set ip address on %s: %v", t.name, errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDarwin) attachLinkLocal() error {
|
||||||
|
var ifr [unix.IFNAMSIZ]byte
|
||||||
|
copy(ifr[:], t.name)
|
||||||
|
|
||||||
|
// attach link-local address
|
||||||
|
fd6, err := unix.Socket(
|
||||||
|
unix.AF_INET6,
|
||||||
|
unix.SOCK_DGRAM,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer syscall.Close(fd6)
|
||||||
|
// SIOCAIFADDR_IN6
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L114-L119
|
||||||
|
// https://opensource.apple.com/source/network_cmds/network_cmds-543.260.3/
|
||||||
|
type in6_addrlifetime struct {
|
||||||
|
//ia6t_expire uint64
|
||||||
|
//ia6t_preferred uint64
|
||||||
|
//ia6t_vltime uint32
|
||||||
|
//ia6t_pltime uint32
|
||||||
|
}
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L336-L343
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6.h#L174-L181
|
||||||
|
type in6_aliasreq struct {
|
||||||
|
ifra_name [unix.IFNAMSIZ]byte
|
||||||
|
ifra_addr unix.RawSockaddrInet6
|
||||||
|
ifra_dstaddr unix.RawSockaddrInet6
|
||||||
|
ifra_prefixmask unix.RawSockaddrInet6
|
||||||
|
ifra_flags int32
|
||||||
|
ifra_lifetime in6_addrlifetime
|
||||||
|
}
|
||||||
|
// Attach link-local address
|
||||||
|
ifra6 := in6_aliasreq{
|
||||||
|
ifra_name: ifr,
|
||||||
|
}
|
||||||
|
if _, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd6),
|
||||||
|
uintptr(_SIOCPROTOATTACH_IN6),
|
||||||
|
uintptr(unsafe.Pointer(&ifra6)),
|
||||||
|
); errno != 0 {
|
||||||
|
return fmt.Errorf("failed to attach link-local address on %s: SIOCPROTOATTACH_IN6 %v", t.name, errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, errno := unix.Syscall(
|
||||||
|
sys_IOCTL,
|
||||||
|
uintptr(fd6),
|
||||||
|
uintptr(_SIOCLL_START),
|
||||||
|
uintptr(unsafe.Pointer(&ifra6)),
|
||||||
|
); errno != 0 {
|
||||||
|
return fmt.Errorf("failed to set ipv6 address on %s: SIOCLL_START %v", t.name, errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAutoDetectInterface get ethernet interface
|
||||||
|
func GetAutoDetectInterface() (string, error) {
|
||||||
|
cmd := exec.Command("bash", "-c", "netstat -rnf inet | grep 'default' | awk -F ' ' 'NR==1{print $6}' | xargs echo -n")
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if out.Len() == 0 {
|
||||||
|
return "", errors.New("interface not found by default route")
|
||||||
|
}
|
||||||
|
return out.String(), nil
|
||||||
|
}
|
254
listener/tun/dev/dev_linux.go
Normal file
254
listener/tun/dev/dev_linux.go
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
// +build linux android
|
||||||
|
|
||||||
|
package dev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cloneDevicePath = "/dev/net/tun"
|
||||||
|
ifReqSize = unix.IFNAMSIZ + 64
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunLinux struct {
|
||||||
|
url string
|
||||||
|
name string
|
||||||
|
tunAddress string
|
||||||
|
autoRoute bool
|
||||||
|
tunFile *os.File
|
||||||
|
mtu int
|
||||||
|
|
||||||
|
closed bool
|
||||||
|
stopOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenTunDevice return a TunDevice according a URL
|
||||||
|
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
deviceURL, _ := url.Parse("dev://clash0")
|
||||||
|
mtu, _ := strconv.ParseInt(deviceURL.Query().Get("mtu"), 0, 32)
|
||||||
|
|
||||||
|
t := &tunLinux{
|
||||||
|
url: deviceURL.String(),
|
||||||
|
mtu: int(mtu),
|
||||||
|
tunAddress: tunAddress,
|
||||||
|
autoRoute: autoRoute,
|
||||||
|
}
|
||||||
|
switch deviceURL.Scheme {
|
||||||
|
case "dev":
|
||||||
|
var err error
|
||||||
|
var dev TunDevice
|
||||||
|
dev, err = t.openDeviceByName(deviceURL.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if autoRoute {
|
||||||
|
SetLinuxAutoRoute()
|
||||||
|
}
|
||||||
|
return dev, nil
|
||||||
|
case "fd":
|
||||||
|
fd, err := strconv.ParseInt(deviceURL.Host, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var dev TunDevice
|
||||||
|
dev, err = t.openDeviceByFd(int(fd))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if autoRoute {
|
||||||
|
SetLinuxAutoRoute()
|
||||||
|
}
|
||||||
|
return dev, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unsupported device type `%s`", deviceURL.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) Name() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) URL() string {
|
||||||
|
return t.url
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) Write(buff []byte) (int, error) {
|
||||||
|
return t.tunFile.Write(buff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) Read(buff []byte) (int, error) {
|
||||||
|
return t.tunFile.Read(buff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) IsClose() bool {
|
||||||
|
return t.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) Close() error {
|
||||||
|
t.stopOnce.Do(func() {
|
||||||
|
if t.autoRoute {
|
||||||
|
RemoveLinuxAutoRoute()
|
||||||
|
}
|
||||||
|
t.closed = true
|
||||||
|
t.tunFile.Close()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) MTU() (int, error) {
|
||||||
|
// Sometime, we can't read MTU by SIOCGIFMTU. Then we should return the preset MTU
|
||||||
|
if t.mtu > 0 {
|
||||||
|
return t.mtu, nil
|
||||||
|
}
|
||||||
|
mtu, err := t.getInterfaceMtu()
|
||||||
|
return int(mtu), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) openDeviceByName(name string) (TunDevice, error) {
|
||||||
|
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifr [ifReqSize]byte
|
||||||
|
var flags uint16 = unix.IFF_TUN | unix.IFF_NO_PI
|
||||||
|
nameBytes := []byte(name)
|
||||||
|
if len(nameBytes) >= unix.IFNAMSIZ {
|
||||||
|
return nil, errors.New("interface name too long")
|
||||||
|
}
|
||||||
|
copy(ifr[:], nameBytes)
|
||||||
|
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags
|
||||||
|
|
||||||
|
_, _, errno := unix.Syscall(
|
||||||
|
unix.SYS_IOCTL,
|
||||||
|
uintptr(nfd),
|
||||||
|
uintptr(unix.TUNSETIFF),
|
||||||
|
uintptr(unsafe.Pointer(&ifr[0])),
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
err = unix.SetNonblock(nfd, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
|
||||||
|
|
||||||
|
t.tunFile = os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||||
|
t.name, err = t.getName()
|
||||||
|
if err != nil {
|
||||||
|
t.tunFile.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) openDeviceByFd(fd int) (TunDevice, error) {
|
||||||
|
var ifr struct {
|
||||||
|
name [16]byte
|
||||||
|
flags uint16
|
||||||
|
_ [22]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := syscall.Dup(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNGETIFF, uintptr(unsafe.Pointer(&ifr)))
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifr.flags&syscall.IFF_TUN == 0 || ifr.flags&syscall.IFF_NO_PI == 0 {
|
||||||
|
return nil, errors.New("only tun device and no pi mode supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
nullStr := ifr.name[:]
|
||||||
|
i := bytes.IndexByte(nullStr, 0)
|
||||||
|
if i != -1 {
|
||||||
|
nullStr = nullStr[:i]
|
||||||
|
}
|
||||||
|
t.name = string(nullStr)
|
||||||
|
t.tunFile = os.NewFile(uintptr(fd), "/dev/tun")
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) getInterfaceMtu() (uint32, error) {
|
||||||
|
fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer syscall.Close(fd)
|
||||||
|
|
||||||
|
var ifreq struct {
|
||||||
|
name [16]byte
|
||||||
|
mtu int32
|
||||||
|
_ [20]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(ifreq.name[:], t.name)
|
||||||
|
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(ifreq.mtu), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunLinux) getName() (string, error) {
|
||||||
|
sysconn, err := t.tunFile.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var ifr [ifReqSize]byte
|
||||||
|
var errno syscall.Errno
|
||||||
|
err = sysconn.Control(func(fd uintptr) {
|
||||||
|
_, _, errno = unix.Syscall(
|
||||||
|
unix.SYS_IOCTL,
|
||||||
|
fd,
|
||||||
|
uintptr(unix.TUNGETIFF),
|
||||||
|
uintptr(unsafe.Pointer(&ifr[0])),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("failed to get name of TUN device: " + err.Error())
|
||||||
|
}
|
||||||
|
if errno != 0 {
|
||||||
|
return "", errors.New("failed to get name of TUN device: " + errno.Error())
|
||||||
|
}
|
||||||
|
nullStr := ifr[:]
|
||||||
|
i := bytes.IndexByte(nullStr, 0)
|
||||||
|
if i != -1 {
|
||||||
|
nullStr = nullStr[:i]
|
||||||
|
}
|
||||||
|
t.name = string(nullStr)
|
||||||
|
return t.name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAutoDetectInterface get ethernet interface
|
||||||
|
func GetAutoDetectInterface() (string, error) {
|
||||||
|
cmd := exec.Command("bash", "-c", "ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return out.String(), nil
|
||||||
|
}
|
17
listener/tun/dev/dev_unsupport.go
Normal file
17
listener/tun/dev/dev_unsupport.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !linux,!android,!darwin,!windows
|
||||||
|
|
||||||
|
package dev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OpenTunDevice(tunAddress string, autoRute bool) (TunDevice, error) {
|
||||||
|
return nil, errors.New("Unsupported platform " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAutoDetectInterface get ethernet interface
|
||||||
|
func GetAutoDetectInterface() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
552
listener/tun/dev/dev_windows.go
Normal file
552
listener/tun/dev/dev_windows.go
Normal file
|
@ -0,0 +1,552 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package dev
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev/winipcfg"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev/wintun"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond)
|
||||||
|
spinloopRateThreshold = 800000000 / 8 // 800mbps
|
||||||
|
spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s
|
||||||
|
|
||||||
|
messageTransportHeaderSize = 0 // size of data preceding content in transport message
|
||||||
|
)
|
||||||
|
|
||||||
|
type rateJuggler struct {
|
||||||
|
current uint64
|
||||||
|
nextByteCount uint64
|
||||||
|
nextStartTime int64
|
||||||
|
changing int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type tunWindows struct {
|
||||||
|
wt *wintun.Adapter
|
||||||
|
handle windows.Handle
|
||||||
|
closed bool
|
||||||
|
closing sync.RWMutex
|
||||||
|
forcedMTU int
|
||||||
|
rate rateJuggler
|
||||||
|
session wintun.Session
|
||||||
|
readWait windows.Handle
|
||||||
|
stopOnce sync.Once
|
||||||
|
|
||||||
|
url string
|
||||||
|
name string
|
||||||
|
tunAddress string
|
||||||
|
autoRoute bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var WintunPool, _ = wintun.MakePool("Clash")
|
||||||
|
var WintunStaticRequestedGUID *windows.GUID
|
||||||
|
|
||||||
|
//go:linkname procyield runtime.procyield
|
||||||
|
func procyield(cycles uint32)
|
||||||
|
|
||||||
|
//go:linkname nanotime runtime.nanotime
|
||||||
|
func nanotime() int64
|
||||||
|
|
||||||
|
// OpenTunDevice return a TunDevice according a URL
|
||||||
|
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
|
||||||
|
requestedGUID, err := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}")
|
||||||
|
if err == nil {
|
||||||
|
WintunStaticRequestedGUID = &requestedGUID
|
||||||
|
log.Debugln("Generate GUID: %s", WintunStaticRequestedGUID.String())
|
||||||
|
} else {
|
||||||
|
log.Warnln("Error parese GUID from string: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaceName := "Clash"
|
||||||
|
mtu := 9000
|
||||||
|
|
||||||
|
tun, err := CreateTUN(interfaceName, mtu, tunAddress, autoRoute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tun, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CreateTUN creates a Wintun interface with the given name. Should a Wintun
|
||||||
|
// interface with the same name exist, it is reused.
|
||||||
|
//
|
||||||
|
func CreateTUN(ifname string, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
return CreateTUNWithRequestedGUID(ifname, WintunStaticRequestedGUID, mtu, tunAddress, autoRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and
|
||||||
|
// a requested GUID. Should a Wintun interface with the same name exist, it is reused.
|
||||||
|
//
|
||||||
|
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
|
||||||
|
var err error
|
||||||
|
var wt *wintun.Adapter
|
||||||
|
|
||||||
|
// Does an interface with this name already exist?
|
||||||
|
wt, err = WintunPool.OpenAdapter(ifname)
|
||||||
|
if err == nil {
|
||||||
|
// If so, we delete it, in case it has weird residual configuration.
|
||||||
|
_, err = wt.Delete(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error deleting already existing interface: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wt, rebootRequired, err := WintunPool.CreateAdapter(ifname, requestedGUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error creating interface: %w", err)
|
||||||
|
}
|
||||||
|
if rebootRequired {
|
||||||
|
log.Infoln("Windows indicated a reboot is required.")
|
||||||
|
}
|
||||||
|
|
||||||
|
forcedMTU := 1420
|
||||||
|
if mtu > 0 {
|
||||||
|
forcedMTU = mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
tun := &tunWindows{
|
||||||
|
wt: wt,
|
||||||
|
handle: windows.InvalidHandle,
|
||||||
|
forcedMTU: forcedMTU,
|
||||||
|
tunAddress: tunAddress,
|
||||||
|
autoRoute: autoRoute,
|
||||||
|
}
|
||||||
|
|
||||||
|
// config tun ip
|
||||||
|
err = tun.configureInterface()
|
||||||
|
if err != nil {
|
||||||
|
tun.wt.Delete(false)
|
||||||
|
return nil, fmt.Errorf("Error configure interface: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
realInterfaceName, err2 := wt.Name()
|
||||||
|
if err2 == nil {
|
||||||
|
ifname = realInterfaceName
|
||||||
|
tun.name = realInterfaceName
|
||||||
|
}
|
||||||
|
|
||||||
|
tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB
|
||||||
|
if err != nil {
|
||||||
|
tun.wt.Delete(false)
|
||||||
|
return nil, fmt.Errorf("Error starting session: %w", err)
|
||||||
|
}
|
||||||
|
tun.readWait = tun.session.ReadWaitEvent()
|
||||||
|
return tun, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) getName() (string, error) {
|
||||||
|
tun.closing.RLock()
|
||||||
|
defer tun.closing.RUnlock()
|
||||||
|
if tun.closed {
|
||||||
|
return "", os.ErrClosed
|
||||||
|
}
|
||||||
|
return tun.wt.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) IsClose() bool {
|
||||||
|
return tun.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) Close() error {
|
||||||
|
tun.stopOnce.Do(func() {
|
||||||
|
//tun.closing.Lock()
|
||||||
|
//defer tun.closing.Unlock()
|
||||||
|
tun.closed = true
|
||||||
|
tun.session.End()
|
||||||
|
if tun.wt != nil {
|
||||||
|
forceCloseSessions := false
|
||||||
|
rebootRequired, err := tun.wt.Delete(forceCloseSessions)
|
||||||
|
if rebootRequired {
|
||||||
|
log.Infoln("Delete Wintun failure, Windows indicated a reboot is required.")
|
||||||
|
} else {
|
||||||
|
log.Infoln("Delete Wintun success.")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Close Wintun Sessions failure: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) MTU() (int, error) {
|
||||||
|
return tun.forcedMTU, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
|
||||||
|
func (tun *tunWindows) ForceMTU(mtu int) {
|
||||||
|
tun.forcedMTU = mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) Read(buff []byte) (int, error) {
|
||||||
|
return tun.ReadO(buff, messageTransportHeaderSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking.
|
||||||
|
|
||||||
|
func (tun *tunWindows) ReadO(buff []byte, offset int) (int, error) {
|
||||||
|
tun.closing.RLock()
|
||||||
|
defer tun.closing.RUnlock()
|
||||||
|
retry:
|
||||||
|
if tun.closed {
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
}
|
||||||
|
start := nanotime()
|
||||||
|
shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2
|
||||||
|
for {
|
||||||
|
if tun.closed {
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
}
|
||||||
|
packet, err := tun.session.ReceivePacket()
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
packetSize := len(packet)
|
||||||
|
copy(buff[offset:], packet)
|
||||||
|
tun.session.ReleaseReceivePacket(packet)
|
||||||
|
tun.rate.update(uint64(packetSize))
|
||||||
|
return packetSize, nil
|
||||||
|
case windows.ERROR_NO_MORE_ITEMS:
|
||||||
|
if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration {
|
||||||
|
windows.WaitForSingleObject(tun.readWait, windows.INFINITE)
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
procyield(1)
|
||||||
|
continue
|
||||||
|
case windows.ERROR_HANDLE_EOF:
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
case windows.ERROR_INVALID_DATA:
|
||||||
|
return 0, errors.New("Send ring corrupt")
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("Read failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) Flush() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) Write(buff []byte) (int, error) {
|
||||||
|
return tun.WriteO(buff, messageTransportHeaderSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) WriteO(buff []byte, offset int) (int, error) {
|
||||||
|
tun.closing.RLock()
|
||||||
|
defer tun.closing.RUnlock()
|
||||||
|
if tun.closed {
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
packetSize := len(buff) - offset
|
||||||
|
tun.rate.update(uint64(packetSize))
|
||||||
|
|
||||||
|
packet, err := tun.session.AllocateSendPacket(packetSize)
|
||||||
|
if err == nil {
|
||||||
|
copy(packet, buff[offset:])
|
||||||
|
tun.session.SendPacket(packet)
|
||||||
|
return packetSize, nil
|
||||||
|
}
|
||||||
|
switch err {
|
||||||
|
case windows.ERROR_HANDLE_EOF:
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
case windows.ERROR_BUFFER_OVERFLOW:
|
||||||
|
return 0, nil // Dropping when ring is full.
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("Write failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LUID returns Windows interface instance ID.
|
||||||
|
func (tun *tunWindows) LUID() uint64 {
|
||||||
|
tun.closing.RLock()
|
||||||
|
defer tun.closing.RUnlock()
|
||||||
|
if tun.closed {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return tun.wt.LUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunningVersion returns the running version of the Wintun driver.
|
||||||
|
func (tun *tunWindows) RunningVersion() (version uint32, err error) {
|
||||||
|
return wintun.RunningVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rate *rateJuggler) update(packetLen uint64) {
|
||||||
|
now := nanotime()
|
||||||
|
total := atomic.AddUint64(&rate.nextByteCount, packetLen)
|
||||||
|
period := uint64(now - atomic.LoadInt64(&rate.nextStartTime))
|
||||||
|
if period >= rateMeasurementGranularity {
|
||||||
|
if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
atomic.StoreInt64(&rate.nextStartTime, now)
|
||||||
|
atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period)
|
||||||
|
atomic.StoreUint64(&rate.nextByteCount, 0)
|
||||||
|
atomic.StoreInt32(&rate.changing, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) Name() string {
|
||||||
|
return tun.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunWindows) URL() string {
|
||||||
|
return fmt.Sprintf("dev://%s", t.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *tunWindows) configureInterface() error {
|
||||||
|
luid := winipcfg.LUID(tun.LUID())
|
||||||
|
|
||||||
|
mtu, err := tun.MTU()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("unable to get device mtu")
|
||||||
|
}
|
||||||
|
|
||||||
|
family := winipcfg.AddressFamily(windows.AF_INET)
|
||||||
|
familyV6 := winipcfg.AddressFamily(windows.AF_INET6)
|
||||||
|
|
||||||
|
tunAddress := winipcfg.ParseIPCidr("198.18.0.1/16")
|
||||||
|
|
||||||
|
addresses := []net.IPNet{tunAddress.IPNet()}
|
||||||
|
|
||||||
|
err = luid.FlushIPAddresses(familyV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = luid.FlushDNS(family)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = luid.FlushDNS(familyV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = luid.FlushRoutes(familyV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = luid.SetIPAddressesForFamily(family, addresses)
|
||||||
|
|
||||||
|
if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
|
||||||
|
cleanupAddressesOnDisconnectedInterfaces(family, addresses)
|
||||||
|
err = luid.SetIPAddressesForFamily(family, addresses)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
foundDefault4 := false
|
||||||
|
foundDefault6 := false
|
||||||
|
|
||||||
|
if tun.autoRoute {
|
||||||
|
allowedIPs := []*winipcfg.IPCidr{
|
||||||
|
winipcfg.ParseIPCidr("1.0.0.0/8"),
|
||||||
|
winipcfg.ParseIPCidr("2.0.0.0/7"),
|
||||||
|
winipcfg.ParseIPCidr("4.0.0.0/6"),
|
||||||
|
winipcfg.ParseIPCidr("8.0.0.0/5"),
|
||||||
|
winipcfg.ParseIPCidr("16.0.0.0/4"),
|
||||||
|
winipcfg.ParseIPCidr("32.0.0.0/3"),
|
||||||
|
winipcfg.ParseIPCidr("64.0.0.0/2"),
|
||||||
|
winipcfg.ParseIPCidr("128.0.0.0/1"),
|
||||||
|
//winipcfg.ParseIPCidr("198.18.0.0/16"),
|
||||||
|
//winipcfg.ParseIPCidr("198.18.0.1/32"),
|
||||||
|
//winipcfg.ParseIPCidr("198.18.255.255/32"),
|
||||||
|
winipcfg.ParseIPCidr("224.0.0.0/4"),
|
||||||
|
winipcfg.ParseIPCidr("255.255.255.255/32"),
|
||||||
|
}
|
||||||
|
|
||||||
|
estimatedRouteCount := len(allowedIPs)
|
||||||
|
routes := make([]winipcfg.RouteData, 0, estimatedRouteCount)
|
||||||
|
var haveV4Address, haveV6Address bool = true, false
|
||||||
|
|
||||||
|
for _, allowedip := range allowedIPs {
|
||||||
|
allowedip.MaskSelf()
|
||||||
|
if (allowedip.Bits() == 32 && !haveV4Address) || (allowedip.Bits() == 128 && !haveV6Address) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
route := winipcfg.RouteData{
|
||||||
|
Destination: allowedip.IPNet(),
|
||||||
|
Metric: 0,
|
||||||
|
}
|
||||||
|
if allowedip.Bits() == 32 {
|
||||||
|
if allowedip.Cidr == 0 {
|
||||||
|
foundDefault4 = true
|
||||||
|
}
|
||||||
|
route.NextHop = net.IPv4zero
|
||||||
|
} else if allowedip.Bits() == 128 {
|
||||||
|
if allowedip.Cidr == 0 {
|
||||||
|
foundDefault6 = true
|
||||||
|
}
|
||||||
|
route.NextHop = net.IPv6zero
|
||||||
|
}
|
||||||
|
routes = append(routes, route)
|
||||||
|
}
|
||||||
|
|
||||||
|
deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes))
|
||||||
|
sort.Slice(routes, func(i, j int) bool {
|
||||||
|
if routes[i].Metric != routes[j].Metric {
|
||||||
|
return routes[i].Metric < routes[j].Metric
|
||||||
|
}
|
||||||
|
if c := bytes.Compare(routes[i].NextHop, routes[j].NextHop); c != 0 {
|
||||||
|
return c < 0
|
||||||
|
}
|
||||||
|
if c := bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP); c != 0 {
|
||||||
|
return c < 0
|
||||||
|
}
|
||||||
|
if c := bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask); c != 0 {
|
||||||
|
return c < 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
for i := 0; i < len(routes); i++ {
|
||||||
|
if i > 0 && routes[i].Metric == routes[i-1].Metric &&
|
||||||
|
bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) &&
|
||||||
|
bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) &&
|
||||||
|
bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deduplicatedRoutes = append(deduplicatedRoutes, &routes[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
err = luid.SetRoutesForFamily(family, deduplicatedRoutes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipif, err := luid.IPInterface(family)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipif.NLMTU = uint32(mtu)
|
||||||
|
|
||||||
|
if family == windows.AF_INET {
|
||||||
|
if foundDefault4 {
|
||||||
|
ipif.UseAutomaticMetric = false
|
||||||
|
ipif.Metric = 0
|
||||||
|
}
|
||||||
|
} else if family == windows.AF_INET6 {
|
||||||
|
if foundDefault6 {
|
||||||
|
ipif.UseAutomaticMetric = false
|
||||||
|
ipif.Metric = 0
|
||||||
|
}
|
||||||
|
ipif.DadTransmits = 0
|
||||||
|
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipif.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipif6, err := luid.IPInterface(familyV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ipif6.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return luid.SetDNS(family, []net.IP{net.ParseIP("198.18.0.2")}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) {
|
||||||
|
if len(addresses) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
includedInAddresses := func(a net.IPNet) bool {
|
||||||
|
// TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer!
|
||||||
|
for _, addr := range addresses {
|
||||||
|
ip := addr.IP
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
ip = ip4
|
||||||
|
}
|
||||||
|
mA, _ := addr.Mask.Size()
|
||||||
|
mB, _ := a.Mask.Size()
|
||||||
|
if bytes.Equal(ip, a.IP) && mA == mB {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.OperStatus == winipcfg.IfOperStatusUp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
|
||||||
|
ip := address.Address.IP()
|
||||||
|
ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))}
|
||||||
|
if includedInAddresses(ipnet) {
|
||||||
|
log.Infoln("[Wintun] Cleaning up stale address %s from interface ‘%s’", ipnet.String(), iface.FriendlyName())
|
||||||
|
iface.LUID.DeleteIPAddress(ipnet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAutoDetectInterface get ethernet interface
|
||||||
|
func GetAutoDetectInterface() (string, error) {
|
||||||
|
ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET))
|
||||||
|
if err == nil {
|
||||||
|
return ifname, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, error) {
|
||||||
|
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagIncludeGateways)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("find ethernet interface failure. %w", err)
|
||||||
|
}
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.OperStatus != winipcfg.IfOperStatusUp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ifname := iface.FriendlyName()
|
||||||
|
if ifname == "Clash" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
|
||||||
|
nextHop := gatewayAddress.Address.IP()
|
||||||
|
|
||||||
|
var ipnet net.IPNet
|
||||||
|
if family == windows.AF_INET {
|
||||||
|
ipnet = net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}
|
||||||
|
} else {
|
||||||
|
ipnet = net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = iface.LUID.Route(ipnet, nextHop); err == nil {
|
||||||
|
return ifname, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("ethernet interface not found")
|
||||||
|
}
|
56
listener/tun/dev/winipcfg/config.go
Normal file
56
listener/tun/dev/winipcfg/config.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IPCidr struct {
|
||||||
|
IP net.IP
|
||||||
|
Cidr uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IPCidr) String() string {
|
||||||
|
return fmt.Sprintf("%s/%d", r.IP.String(), r.Cidr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IPCidr) Bits() uint8 {
|
||||||
|
if r.IP.To4() != nil {
|
||||||
|
return 32
|
||||||
|
}
|
||||||
|
return 128
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IPCidr) IPNet() net.IPNet {
|
||||||
|
return net.IPNet{
|
||||||
|
IP: r.IP,
|
||||||
|
Mask: net.CIDRMask(int(r.Cidr), int(r.Bits())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IPCidr) MaskSelf() {
|
||||||
|
bits := int(r.Bits())
|
||||||
|
mask := net.CIDRMask(int(r.Cidr), bits)
|
||||||
|
for i := 0; i < bits/8; i++ {
|
||||||
|
r.IP[i] &= mask[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseIPCidr(ipcidr string) *IPCidr {
|
||||||
|
s := strings.Split(ipcidr, "/")
|
||||||
|
if len(s) != 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cidr, err := strconv.Atoi(s[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &IPCidr{
|
||||||
|
IP: net.ParseIP(s[0]),
|
||||||
|
Cidr: uint8(cidr),
|
||||||
|
}
|
||||||
|
}
|
85
listener/tun/dev/winipcfg/interface_change_handler.go
Normal file
85
listener/tun/dev/winipcfg/interface_change_handler.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InterfaceChangeCallback structure allows interface change callback handling.
|
||||||
|
type InterfaceChangeCallback struct {
|
||||||
|
cb func(notificationType MibNotificationType, iface *MibIPInterfaceRow)
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
interfaceChangeAddRemoveMutex = sync.Mutex{}
|
||||||
|
interfaceChangeMutex = sync.Mutex{}
|
||||||
|
interfaceChangeCallbacks = make(map[*InterfaceChangeCallback]bool)
|
||||||
|
interfaceChangeHandle = windows.Handle(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterInterfaceChangeCallback registers a new InterfaceChangeCallback. If this particular callback is already
|
||||||
|
// registered, the function will silently return. Returned InterfaceChangeCallback.Unregister method should be used
|
||||||
|
// to unregister.
|
||||||
|
func RegisterInterfaceChangeCallback(callback func(notificationType MibNotificationType, iface *MibIPInterfaceRow)) (*InterfaceChangeCallback, error) {
|
||||||
|
s := &InterfaceChangeCallback{cb: callback}
|
||||||
|
|
||||||
|
interfaceChangeAddRemoveMutex.Lock()
|
||||||
|
defer interfaceChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
interfaceChangeMutex.Lock()
|
||||||
|
defer interfaceChangeMutex.Unlock()
|
||||||
|
|
||||||
|
interfaceChangeCallbacks[s] = true
|
||||||
|
|
||||||
|
if interfaceChangeHandle == 0 {
|
||||||
|
err := notifyIPInterfaceChange(windows.AF_UNSPEC, windows.NewCallback(interfaceChanged), 0, false, &interfaceChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
delete(interfaceChangeCallbacks, s)
|
||||||
|
interfaceChangeHandle = 0
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister unregisters the callback.
|
||||||
|
func (callback *InterfaceChangeCallback) Unregister() error {
|
||||||
|
interfaceChangeAddRemoveMutex.Lock()
|
||||||
|
defer interfaceChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
interfaceChangeMutex.Lock()
|
||||||
|
delete(interfaceChangeCallbacks, callback)
|
||||||
|
removeIt := len(interfaceChangeCallbacks) == 0 && interfaceChangeHandle != 0
|
||||||
|
interfaceChangeMutex.Unlock()
|
||||||
|
|
||||||
|
callback.wait.Wait()
|
||||||
|
|
||||||
|
if removeIt {
|
||||||
|
err := cancelMibChangeNotify2(interfaceChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
interfaceChangeHandle = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func interfaceChanged(callerContext uintptr, row *MibIPInterfaceRow, notificationType MibNotificationType) uintptr {
|
||||||
|
rowCopy := *row
|
||||||
|
interfaceChangeMutex.Lock()
|
||||||
|
for cb := range interfaceChangeCallbacks {
|
||||||
|
cb.wait.Add(1)
|
||||||
|
go func(cb *InterfaceChangeCallback) {
|
||||||
|
cb.cb(notificationType, &rowCopy)
|
||||||
|
cb.wait.Done()
|
||||||
|
}(cb)
|
||||||
|
}
|
||||||
|
interfaceChangeMutex.Unlock()
|
||||||
|
return 0
|
||||||
|
}
|
383
listener/tun/dev/winipcfg/luid.go
Normal file
383
listener/tun/dev/winipcfg/luid.go
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LUID represents a network interface.
|
||||||
|
type LUID uint64
|
||||||
|
|
||||||
|
// IPInterface method retrieves IP information for the specified interface on the local computer.
|
||||||
|
func (luid LUID) IPInterface(family AddressFamily) (*MibIPInterfaceRow, error) {
|
||||||
|
row := &MibIPInterfaceRow{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
row.Family = family
|
||||||
|
err := row.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface method retrieves information for the specified adapter on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2
|
||||||
|
func (luid LUID) Interface() (*MibIfRow2, error) {
|
||||||
|
row := &MibIfRow2{}
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUID method converts a locally unique identifier (LUID) for a network interface to a globally unique identifier (GUID) for the interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceluidtoguid
|
||||||
|
func (luid LUID) GUID() (*windows.GUID, error) {
|
||||||
|
guid := &windows.GUID{}
|
||||||
|
err := convertInterfaceLUIDToGUID(&luid, guid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return guid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LUIDFromGUID function converts a globally unique identifier (GUID) for a network interface to the locally unique identifier (LUID) for the interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
|
||||||
|
func LUIDFromGUID(guid *windows.GUID) (LUID, error) {
|
||||||
|
var luid LUID
|
||||||
|
err := convertInterfaceGUIDToLUID(guid, &luid)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return luid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LUIDFromIndex function converts a local index for a network interface to the locally unique identifier (LUID) for the interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceindextoluid
|
||||||
|
func LUIDFromIndex(index uint32) (LUID, error) {
|
||||||
|
var luid LUID
|
||||||
|
err := convertInterfaceIndexToLUID(index, &luid)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return luid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAddress method returns MibUnicastIPAddressRow struct that matches to provided 'ip' argument. Corresponds to GetUnicastIpAddressEntry
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry)
|
||||||
|
func (luid LUID) IPAddress(ip net.IP) (*MibUnicastIPAddressRow, error) {
|
||||||
|
row := &MibUnicastIPAddressRow{InterfaceLUID: luid}
|
||||||
|
|
||||||
|
err := row.Address.SetIP(ip, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = row.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIPAddress method adds new unicast IP address to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
func (luid LUID) AddIPAddress(address net.IPNet) error {
|
||||||
|
row := &MibUnicastIPAddressRow{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.Address.SetIP(address.IP, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ones, _ := address.Mask.Size()
|
||||||
|
row.OnLinkPrefixLength = uint8(ones)
|
||||||
|
return row.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIPAddresses method adds multiple new unicast IP addresses to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
func (luid LUID) AddIPAddresses(addresses []net.IPNet) error {
|
||||||
|
for i := range addresses {
|
||||||
|
err := luid.AddIPAddress(addresses[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIPAddresses method sets new unicast IP addresses to the interface.
|
||||||
|
func (luid LUID) SetIPAddresses(addresses []net.IPNet) error {
|
||||||
|
err := luid.FlushIPAddresses(windows.AF_UNSPEC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return luid.AddIPAddresses(addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIPAddressesForFamily method sets new unicast IP addresses for a specific family to the interface.
|
||||||
|
func (luid LUID) SetIPAddressesForFamily(family AddressFamily, addresses []net.IPNet) error {
|
||||||
|
err := luid.FlushIPAddresses(family)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range addresses {
|
||||||
|
asV4 := addresses[i].IP.To4()
|
||||||
|
if asV4 == nil && family == windows.AF_INET {
|
||||||
|
continue
|
||||||
|
} else if asV4 != nil && family == windows.AF_INET6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := luid.AddIPAddress(addresses[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIPAddress method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry).
|
||||||
|
func (luid LUID) DeleteIPAddress(address net.IPNet) error {
|
||||||
|
row := &MibUnicastIPAddressRow{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.Address.SetIP(address.IP, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Note: OnLinkPrefixLength member is ignored by DeleteUnicastIpAddressEntry().
|
||||||
|
ones, _ := address.Mask.Size()
|
||||||
|
row.OnLinkPrefixLength = uint8(ones)
|
||||||
|
return row.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushIPAddresses method deletes all interface's unicast IP addresses.
|
||||||
|
func (luid LUID) FlushIPAddresses(family AddressFamily) error {
|
||||||
|
var tab *mibUnicastIPAddressTable
|
||||||
|
err := getUnicastIPAddressTable(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t := tab.get()
|
||||||
|
for i := range t {
|
||||||
|
if t[i].InterfaceLUID == luid {
|
||||||
|
t[i].Delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab.free()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route method returns route determined with the input arguments. Corresponds to GetIpForwardEntry2 function
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2).
|
||||||
|
// NOTE: If the corresponding route isn't found, the method will return error.
|
||||||
|
func (luid LUID) Route(destination net.IPNet, nextHop net.IP) (*MibIPforwardRow2, error) {
|
||||||
|
row := &MibIPforwardRow2{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.DestinationPrefix.SetIPNet(destination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = row.NextHop.SetIP(nextHop, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = row.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute method adds a route to the interface. Corresponds to CreateIpForwardEntry2 function, with added splitDefault feature.
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2)
|
||||||
|
func (luid LUID) AddRoute(destination net.IPNet, nextHop net.IP, metric uint32) error {
|
||||||
|
row := &MibIPforwardRow2{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.DestinationPrefix.SetIPNet(destination)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = row.NextHop.SetIP(nextHop, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
row.Metric = metric
|
||||||
|
return row.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoutes method adds multiple routes to the interface.
|
||||||
|
func (luid LUID) AddRoutes(routesData []*RouteData) error {
|
||||||
|
for _, rd := range routesData {
|
||||||
|
err := luid.AddRoute(rd.Destination, rd.NextHop, rd.Metric)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoutes method sets (flush than add) multiple routes to the interface.
|
||||||
|
func (luid LUID) SetRoutes(routesData []*RouteData) error {
|
||||||
|
err := luid.FlushRoutes(windows.AF_UNSPEC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return luid.AddRoutes(routesData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoutesForFamily method sets (flush than add) multiple routes for a specific family to the interface.
|
||||||
|
func (luid LUID) SetRoutesForFamily(family AddressFamily, routesData []*RouteData) error {
|
||||||
|
err := luid.FlushRoutes(family)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, rd := range routesData {
|
||||||
|
asV4 := rd.Destination.IP.To4()
|
||||||
|
if asV4 == nil && family == windows.AF_INET {
|
||||||
|
continue
|
||||||
|
} else if asV4 != nil && family == windows.AF_INET6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := luid.AddRoute(rd.Destination, rd.NextHop, rd.Metric)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRoute method deletes a route that matches the criteria. Corresponds to DeleteIpForwardEntry2 function
|
||||||
|
// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2).
|
||||||
|
func (luid LUID) DeleteRoute(destination net.IPNet, nextHop net.IP) error {
|
||||||
|
row := &MibIPforwardRow2{}
|
||||||
|
row.Init()
|
||||||
|
row.InterfaceLUID = luid
|
||||||
|
err := row.DestinationPrefix.SetIPNet(destination)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = row.NextHop.SetIP(nextHop, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = row.get()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return row.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushRoutes method deletes all interface's routes.
|
||||||
|
// It continues on failures, and returns the last error afterwards.
|
||||||
|
func (luid LUID) FlushRoutes(family AddressFamily) error {
|
||||||
|
var tab *mibIPforwardTable2
|
||||||
|
err := getIPForwardTable2(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t := tab.get()
|
||||||
|
for i := range t {
|
||||||
|
if t[i].InterfaceLUID == luid {
|
||||||
|
err2 := t[i].Delete()
|
||||||
|
if err2 != nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab.free()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS method returns all DNS server addresses associated with the adapter.
|
||||||
|
func (luid LUID) DNS() ([]net.IP, error) {
|
||||||
|
addresses, err := GetAdaptersAddresses(windows.AF_UNSPEC, GAAFlagDefault)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := make([]net.IP, 0, len(addresses))
|
||||||
|
for _, addr := range addresses {
|
||||||
|
if addr.LUID == luid {
|
||||||
|
for dns := addr.FirstDNSServerAddress; dns != nil; dns = dns.Next {
|
||||||
|
if ip := dns.Address.IP(); ip != nil {
|
||||||
|
r = append(r, ip)
|
||||||
|
} else {
|
||||||
|
return nil, windows.ERROR_INVALID_PARAMETER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDNS method clears previous and associates new DNS servers and search domains with the adapter for a specific family.
|
||||||
|
func (luid LUID) SetDNS(family AddressFamily, servers []net.IP, domains []string) error {
|
||||||
|
if family != windows.AF_INET && family != windows.AF_INET6 {
|
||||||
|
return windows.ERROR_PROTOCOL_UNREACHABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredServers []string
|
||||||
|
for _, server := range servers {
|
||||||
|
if v4 := server.To4(); v4 != nil && family == windows.AF_INET {
|
||||||
|
filteredServers = append(filteredServers, v4.String())
|
||||||
|
} else if v6 := server.To16(); v4 == nil && v6 != nil && family == windows.AF_INET6 {
|
||||||
|
filteredServers = append(filteredServers, v6.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
servers16, err := windows.UTF16PtrFromString(strings.Join(filteredServers, ","))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
domains16, err := windows.UTF16PtrFromString(strings.Join(domains, ","))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
guid, err := luid.GUID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var maybeV6 uint64
|
||||||
|
if family == windows.AF_INET6 {
|
||||||
|
maybeV6 = disFlagsIPv6
|
||||||
|
}
|
||||||
|
// For >= Windows 10 1809
|
||||||
|
err = setInterfaceDnsSettings(*guid, &dnsInterfaceSettings{
|
||||||
|
Version: disVersion1,
|
||||||
|
Flags: disFlagsNameServer | disFlagsSearchList | maybeV6,
|
||||||
|
NameServer: servers16,
|
||||||
|
SearchList: domains16,
|
||||||
|
})
|
||||||
|
if err == nil || !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// For < Windows 10 1809
|
||||||
|
err = luid.fallbackSetDNSForFamily(family, servers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(domains) > 0 {
|
||||||
|
return luid.fallbackSetDNSDomain(domains[0])
|
||||||
|
} else {
|
||||||
|
return luid.fallbackSetDNSDomain("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushDNS method clears all DNS servers associated with the adapter.
|
||||||
|
func (luid LUID) FlushDNS(family AddressFamily) error {
|
||||||
|
return luid.SetDNS(family, nil, nil)
|
||||||
|
}
|
3
listener/tun/dev/winipcfg/mksyscall.go
Normal file
3
listener/tun/dev/winipcfg/mksyscall.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zwinipcfg_windows.go winipcfg.go
|
105
listener/tun/dev/winipcfg/netsh.go
Normal file
105
listener/tun/dev/winipcfg/netsh.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runNetsh(cmds []string) error {
|
||||||
|
system32, err := windows.GetSystemDirectory()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command(filepath.Join(system32, "netsh.exe"))
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("runNetsh stdin pipe - %w", err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
io.WriteString(stdin, strings.Join(append(cmds, "exit\r\n"), "\r\n"))
|
||||||
|
}()
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
// Horrible kludges, sorry.
|
||||||
|
cleaned := bytes.ReplaceAll(output, []byte{'\r', '\n'}, []byte{'\n'})
|
||||||
|
cleaned = bytes.ReplaceAll(cleaned, []byte("netsh>"), []byte{})
|
||||||
|
cleaned = bytes.ReplaceAll(cleaned, []byte("There are no Domain Name Servers (DNS) configured on this computer."), []byte{})
|
||||||
|
cleaned = bytes.TrimSpace(cleaned)
|
||||||
|
if len(cleaned) != 0 && err == nil {
|
||||||
|
return fmt.Errorf("netsh: %#q", string(cleaned))
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("netsh: %v: %#q", err, string(cleaned))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no register=both"
|
||||||
|
netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no register=both"
|
||||||
|
netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no"
|
||||||
|
netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (luid LUID) fallbackSetDNSForFamily(family AddressFamily, dnses []net.IP) error {
|
||||||
|
var templateFlush string
|
||||||
|
if family == windows.AF_INET {
|
||||||
|
templateFlush = netshCmdTemplateFlush4
|
||||||
|
} else if family == windows.AF_INET6 {
|
||||||
|
templateFlush = netshCmdTemplateFlush6
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds := make([]string, 0, 1+len(dnses))
|
||||||
|
ipif, err := luid.IPInterface(family)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmds = append(cmds, fmt.Sprintf(templateFlush, ipif.InterfaceIndex))
|
||||||
|
for i := 0; i < len(dnses); i++ {
|
||||||
|
if v4 := dnses[i].To4(); v4 != nil && family == windows.AF_INET {
|
||||||
|
cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd4, ipif.InterfaceIndex, v4.String()))
|
||||||
|
} else if v6 := dnses[i].To16(); v4 == nil && v6 != nil && family == windows.AF_INET6 {
|
||||||
|
cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd6, ipif.InterfaceIndex, v6.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runNetsh(cmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (luid LUID) fallbackSetDNSDomain(domain string) error {
|
||||||
|
guid, err := luid.GUID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error converting luid to guid: %w", err)
|
||||||
|
}
|
||||||
|
key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%v", guid), registry.QUERY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error opening adapter-specific TCP/IP network registry key: %w", err)
|
||||||
|
}
|
||||||
|
paths, _, err := key.GetStringsValue("IpConfig")
|
||||||
|
key.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error reading IpConfig registry key: %w", err)
|
||||||
|
}
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return errors.New("No TCP/IP interfaces found on adapter")
|
||||||
|
}
|
||||||
|
key, err = registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\%s", paths[0]), registry.SET_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to open TCP/IP network registry key: %w", err)
|
||||||
|
}
|
||||||
|
err = key.SetStringValue("Domain", domain)
|
||||||
|
key.Close()
|
||||||
|
return err
|
||||||
|
}
|
85
listener/tun/dev/winipcfg/route_change_handler.go
Normal file
85
listener/tun/dev/winipcfg/route_change_handler.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouteChangeCallback structure allows route change callback handling.
|
||||||
|
type RouteChangeCallback struct {
|
||||||
|
cb func(notificationType MibNotificationType, route *MibIPforwardRow2)
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
routeChangeAddRemoveMutex = sync.Mutex{}
|
||||||
|
routeChangeMutex = sync.Mutex{}
|
||||||
|
routeChangeCallbacks = make(map[*RouteChangeCallback]bool)
|
||||||
|
routeChangeHandle = windows.Handle(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterRouteChangeCallback registers a new RouteChangeCallback. If this particular callback is already
|
||||||
|
// registered, the function will silently return. Returned RouteChangeCallback.Unregister method should be used
|
||||||
|
// to unregister.
|
||||||
|
func RegisterRouteChangeCallback(callback func(notificationType MibNotificationType, route *MibIPforwardRow2)) (*RouteChangeCallback, error) {
|
||||||
|
s := &RouteChangeCallback{cb: callback}
|
||||||
|
|
||||||
|
routeChangeAddRemoveMutex.Lock()
|
||||||
|
defer routeChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
routeChangeMutex.Lock()
|
||||||
|
defer routeChangeMutex.Unlock()
|
||||||
|
|
||||||
|
routeChangeCallbacks[s] = true
|
||||||
|
|
||||||
|
if routeChangeHandle == 0 {
|
||||||
|
err := notifyRouteChange2(windows.AF_UNSPEC, windows.NewCallback(routeChanged), 0, false, &routeChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
delete(routeChangeCallbacks, s)
|
||||||
|
routeChangeHandle = 0
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister unregisters the callback.
|
||||||
|
func (callback *RouteChangeCallback) Unregister() error {
|
||||||
|
routeChangeAddRemoveMutex.Lock()
|
||||||
|
defer routeChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
routeChangeMutex.Lock()
|
||||||
|
delete(routeChangeCallbacks, callback)
|
||||||
|
removeIt := len(routeChangeCallbacks) == 0 && routeChangeHandle != 0
|
||||||
|
routeChangeMutex.Unlock()
|
||||||
|
|
||||||
|
callback.wait.Wait()
|
||||||
|
|
||||||
|
if removeIt {
|
||||||
|
err := cancelMibChangeNotify2(routeChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
routeChangeHandle = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func routeChanged(callerContext uintptr, row *MibIPforwardRow2, notificationType MibNotificationType) uintptr {
|
||||||
|
rowCopy := *row
|
||||||
|
routeChangeMutex.Lock()
|
||||||
|
for cb := range routeChangeCallbacks {
|
||||||
|
cb.wait.Add(1)
|
||||||
|
go func(cb *RouteChangeCallback) {
|
||||||
|
cb.cb(notificationType, &rowCopy)
|
||||||
|
cb.wait.Done()
|
||||||
|
}(cb)
|
||||||
|
}
|
||||||
|
routeChangeMutex.Unlock()
|
||||||
|
return 0
|
||||||
|
}
|
993
listener/tun/dev/winipcfg/types.go
Normal file
993
listener/tun/dev/winipcfg/types.go
Normal file
|
@ -0,0 +1,993 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
anySize = 1
|
||||||
|
maxDNSSuffixStringLength = 256
|
||||||
|
maxDHCPv6DUIDLength = 130
|
||||||
|
ifMaxStringSize = 256
|
||||||
|
ifMaxPhysAddressLength = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddressFamily enumeration specifies protocol family and is one of the windows.AF_* constants.
|
||||||
|
type AddressFamily uint16
|
||||||
|
|
||||||
|
// IPAAFlags enumeration describes adapter addresses flags
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_addresses_lh
|
||||||
|
type IPAAFlags uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
IPAAFlagDdnsEnabled IPAAFlags = 1 << iota
|
||||||
|
IPAAFlagRegisterAdapterSuffix
|
||||||
|
IPAAFlagDhcpv4Enabled
|
||||||
|
IPAAFlagReceiveOnly
|
||||||
|
IPAAFlagNoMulticast
|
||||||
|
IPAAFlagIpv6OtherStatefulConfig
|
||||||
|
IPAAFlagNetbiosOverTcpipEnabled
|
||||||
|
IPAAFlagIpv4Enabled
|
||||||
|
IPAAFlagIpv6Enabled
|
||||||
|
IPAAFlagIpv6ManagedAddressConfigurationSupported
|
||||||
|
)
|
||||||
|
|
||||||
|
// IfOperStatus enumeration specifies the operational status of an interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-if_oper_status
|
||||||
|
type IfOperStatus uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
IfOperStatusUp IfOperStatus = iota + 1
|
||||||
|
IfOperStatusDown
|
||||||
|
IfOperStatusTesting
|
||||||
|
IfOperStatusUnknown
|
||||||
|
IfOperStatusDormant
|
||||||
|
IfOperStatusNotPresent
|
||||||
|
IfOperStatusLowerLayerDown
|
||||||
|
)
|
||||||
|
|
||||||
|
// IfType enumeration specifies interface type.
|
||||||
|
type IfType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
IfTypeOther IfType = 1 // None of the below
|
||||||
|
IfTypeRegular1822 = 2
|
||||||
|
IfTypeHdh1822 = 3
|
||||||
|
IfTypeDdnX25 = 4
|
||||||
|
IfTypeRfc877X25 = 5
|
||||||
|
IfTypeEthernetCSMACD = 6
|
||||||
|
IfTypeISO88023CSMACD = 7
|
||||||
|
IfTypeISO88024Tokenbus = 8
|
||||||
|
IfTypeISO88025Tokenring = 9
|
||||||
|
IfTypeISO88026Man = 10
|
||||||
|
IfTypeStarlan = 11
|
||||||
|
IfTypeProteon10Mbit = 12
|
||||||
|
IfTypeProteon80Mbit = 13
|
||||||
|
IfTypeHyperchannel = 14
|
||||||
|
IfTypeFddi = 15
|
||||||
|
IfTypeLapB = 16
|
||||||
|
IfTypeSdlc = 17
|
||||||
|
IfTypeDs1 = 18 // DS1-MIB
|
||||||
|
IfTypeE1 = 19 // Obsolete; see DS1-MIB
|
||||||
|
IfTypeBasicISDN = 20
|
||||||
|
IfTypePrimaryISDN = 21
|
||||||
|
IfTypePropPoint2PointSerial = 22 // proprietary serial
|
||||||
|
IfTypePPP = 23
|
||||||
|
IfTypeSoftwareLoopback = 24
|
||||||
|
IfTypeEon = 25 // CLNP over IP
|
||||||
|
IfTypeEthernet3Mbit = 26
|
||||||
|
IfTypeNsip = 27 // XNS over IP
|
||||||
|
IfTypeSlip = 28 // Generic Slip
|
||||||
|
IfTypeUltra = 29 // ULTRA Technologies
|
||||||
|
IfTypeDs3 = 30 // DS3-MIB
|
||||||
|
IfTypeSip = 31 // SMDS, coffee
|
||||||
|
IfTypeFramerelay = 32 // DTE only
|
||||||
|
IfTypeRs232 = 33
|
||||||
|
IfTypePara = 34 // Parallel port
|
||||||
|
IfTypeArcnet = 35
|
||||||
|
IfTypeArcnetPlus = 36
|
||||||
|
IfTypeAtm = 37 // ATM cells
|
||||||
|
IfTypeMioX25 = 38
|
||||||
|
IfTypeSonet = 39 // SONET or SDH
|
||||||
|
IfTypeX25Ple = 40
|
||||||
|
IfTypeIso88022LLC = 41
|
||||||
|
IfTypeLocaltalk = 42
|
||||||
|
IfTypeSmdsDxi = 43
|
||||||
|
IfTypeFramerelayService = 44 // FRNETSERV-MIB
|
||||||
|
IfTypeV35 = 45
|
||||||
|
IfTypeHssi = 46
|
||||||
|
IfTypeHippi = 47
|
||||||
|
IfTypeModem = 48 // Generic Modem
|
||||||
|
IfTypeAal5 = 49 // AAL5 over ATM
|
||||||
|
IfTypeSonetPath = 50
|
||||||
|
IfTypeSonetVt = 51
|
||||||
|
IfTypeSmdsIcip = 52 // SMDS InterCarrier Interface
|
||||||
|
IfTypePropVirtual = 53 // Proprietary virtual/internal
|
||||||
|
IfTypePropMultiplexor = 54 // Proprietary multiplexing
|
||||||
|
IfTypeIEEE80212 = 55 // 100BaseVG
|
||||||
|
IfTypeFibrechannel = 56
|
||||||
|
IfTypeHippiinterface = 57
|
||||||
|
IfTypeFramerelayInterconnect = 58 // Obsolete, use 32 or 44
|
||||||
|
IfTypeAflane8023 = 59 // ATM Emulated LAN for 802.3
|
||||||
|
IfTypeAflane8025 = 60 // ATM Emulated LAN for 802.5
|
||||||
|
IfTypeCctemul = 61 // ATM Emulated circuit
|
||||||
|
IfTypeFastether = 62 // Fast Ethernet (100BaseT)
|
||||||
|
IfTypeISDN = 63 // ISDN and X.25
|
||||||
|
IfTypeV11 = 64 // CCITT V.11/X.21
|
||||||
|
IfTypeV36 = 65 // CCITT V.36
|
||||||
|
IfTypeG703_64k = 66 // CCITT G703 at 64Kbps
|
||||||
|
IfTypeG703_2mb = 67 // Obsolete; see DS1-MIB
|
||||||
|
IfTypeQllc = 68 // SNA QLLC
|
||||||
|
IfTypeFastetherFX = 69 // Fast Ethernet (100BaseFX)
|
||||||
|
IfTypeChannel = 70
|
||||||
|
IfTypeIEEE80211 = 71 // Radio spread spectrum
|
||||||
|
IfTypeIBM370parchan = 72 // IBM System 360/370 OEMI Channel
|
||||||
|
IfTypeEscon = 73 // IBM Enterprise Systems Connection
|
||||||
|
IfTypeDlsw = 74 // Data Link Switching
|
||||||
|
IfTypeISDNS = 75 // ISDN S/T interface
|
||||||
|
IfTypeISDNU = 76 // ISDN U interface
|
||||||
|
IfTypeLapD = 77 // Link Access Protocol D
|
||||||
|
IfTypeIpswitch = 78 // IP Switching Objects
|
||||||
|
IfTypeRsrb = 79 // Remote Source Route Bridging
|
||||||
|
IfTypeAtmLogical = 80 // ATM Logical Port
|
||||||
|
IfTypeDs0 = 81 // Digital Signal Level 0
|
||||||
|
IfTypeDs0Bundle = 82 // Group of ds0s on the same ds1
|
||||||
|
IfTypeBsc = 83 // Bisynchronous Protocol
|
||||||
|
IfTypeAsync = 84 // Asynchronous Protocol
|
||||||
|
IfTypeCnr = 85 // Combat Net Radio
|
||||||
|
IfTypeIso88025rDtr = 86 // ISO 802.5r DTR
|
||||||
|
IfTypeEplrs = 87 // Ext Pos Loc Report Sys
|
||||||
|
IfTypeArap = 88 // Appletalk Remote Access Protocol
|
||||||
|
IfTypePropCnls = 89 // Proprietary Connectionless Proto
|
||||||
|
IfTypeHostpad = 90 // CCITT-ITU X.29 PAD Protocol
|
||||||
|
IfTypeTermpad = 91 // CCITT-ITU X.3 PAD Facility
|
||||||
|
IfTypeFramerelayMpi = 92 // Multiproto Interconnect over FR
|
||||||
|
IfTypeX213 = 93 // CCITT-ITU X213
|
||||||
|
IfTypeAdsl = 94 // Asymmetric Digital Subscrbr Loop
|
||||||
|
IfTypeRadsl = 95 // Rate-Adapt Digital Subscrbr Loop
|
||||||
|
IfTypeSdsl = 96 // Symmetric Digital Subscriber Loop
|
||||||
|
IfTypeVdsl = 97 // Very H-Speed Digital Subscrb Loop
|
||||||
|
IfTypeIso88025Crfprint = 98 // ISO 802.5 CRFP
|
||||||
|
IfTypeMyrinet = 99 // Myricom Myrinet
|
||||||
|
IfTypeVoiceEm = 100 // Voice recEive and transMit
|
||||||
|
IfTypeVoiceFxo = 101 // Voice Foreign Exchange Office
|
||||||
|
IfTypeVoiceFxs = 102 // Voice Foreign Exchange Station
|
||||||
|
IfTypeVoiceEncap = 103 // Voice encapsulation
|
||||||
|
IfTypeVoiceOverip = 104 // Voice over IP encapsulation
|
||||||
|
IfTypeAtmDxi = 105 // ATM DXI
|
||||||
|
IfTypeAtmFuni = 106 // ATM FUNI
|
||||||
|
IfTypeAtmIma = 107 // ATM IMA
|
||||||
|
IfTypePPPmultilinkbundle = 108 // PPP Multilink Bundle
|
||||||
|
IfTypeIpoverCdlc = 109 // IBM ipOverCdlc
|
||||||
|
IfTypeIpoverClaw = 110 // IBM Common Link Access to Workstn
|
||||||
|
IfTypeStacktostack = 111 // IBM stackToStack
|
||||||
|
IfTypeVirtualipaddress = 112 // IBM VIPA
|
||||||
|
IfTypeMpc = 113 // IBM multi-proto channel support
|
||||||
|
IfTypeIpoverAtm = 114 // IBM ipOverAtm
|
||||||
|
IfTypeIso88025Fiber = 115 // ISO 802.5j Fiber Token Ring
|
||||||
|
IfTypeTdlc = 116 // IBM twinaxial data link control
|
||||||
|
IfTypeGigabitethernet = 117
|
||||||
|
IfTypeHdlc = 118
|
||||||
|
IfTypeLapF = 119
|
||||||
|
IfTypeV37 = 120
|
||||||
|
IfTypeX25Mlp = 121 // Multi-Link Protocol
|
||||||
|
IfTypeX25Huntgroup = 122 // X.25 Hunt Group
|
||||||
|
IfTypeTransphdlc = 123
|
||||||
|
IfTypeInterleave = 124 // Interleave channel
|
||||||
|
IfTypeFast = 125 // Fast channel
|
||||||
|
IfTypeIP = 126 // IP (for APPN HPR in IP networks)
|
||||||
|
IfTypeDocscableMaclayer = 127 // CATV Mac Layer
|
||||||
|
IfTypeDocscableDownstream = 128 // CATV Downstream interface
|
||||||
|
IfTypeDocscableUpstream = 129 // CATV Upstream interface
|
||||||
|
IfTypeA12mppswitch = 130 // Avalon Parallel Processor
|
||||||
|
IfTypeTunnel = 131 // Encapsulation interface
|
||||||
|
IfTypeCoffee = 132 // Coffee pot
|
||||||
|
IfTypeCes = 133 // Circuit Emulation Service
|
||||||
|
IfTypeAtmSubinterface = 134 // ATM Sub Interface
|
||||||
|
IfTypeL2Vlan = 135 // Layer 2 Virtual LAN using 802.1Q
|
||||||
|
IfTypeL3Ipvlan = 136 // Layer 3 Virtual LAN using IP
|
||||||
|
IfTypeL3Ipxvlan = 137 // Layer 3 Virtual LAN using IPX
|
||||||
|
IfTypeDigitalpowerline = 138 // IP over Power Lines
|
||||||
|
IfTypeMediamailoverip = 139 // Multimedia Mail over IP
|
||||||
|
IfTypeDtm = 140 // Dynamic syncronous Transfer Mode
|
||||||
|
IfTypeDcn = 141 // Data Communications Network
|
||||||
|
IfTypeIpforward = 142 // IP Forwarding Interface
|
||||||
|
IfTypeMsdsl = 143 // Multi-rate Symmetric DSL
|
||||||
|
IfTypeIEEE1394 = 144 // IEEE1394 High Perf Serial Bus
|
||||||
|
IfTypeIfGsn = 145
|
||||||
|
IfTypeDvbrccMaclayer = 146
|
||||||
|
IfTypeDvbrccDownstream = 147
|
||||||
|
IfTypeDvbrccUpstream = 148
|
||||||
|
IfTypeAtmVirtual = 149
|
||||||
|
IfTypeMplsTunnel = 150
|
||||||
|
IfTypeSrp = 151
|
||||||
|
IfTypeVoiceoveratm = 152
|
||||||
|
IfTypeVoiceoverframerelay = 153
|
||||||
|
IfTypeIdsl = 154
|
||||||
|
IfTypeCompositelink = 155
|
||||||
|
IfTypeSs7Siglink = 156
|
||||||
|
IfTypePropWirelessP2P = 157
|
||||||
|
IfTypeFrForward = 158
|
||||||
|
IfTypeRfc1483 = 159
|
||||||
|
IfTypeUsb = 160
|
||||||
|
IfTypeIEEE8023adLag = 161
|
||||||
|
IfTypeBgpPolicyAccounting = 162
|
||||||
|
IfTypeFrf16MfrBundle = 163
|
||||||
|
IfTypeH323Gatekeeper = 164
|
||||||
|
IfTypeH323Proxy = 165
|
||||||
|
IfTypeMpls = 166
|
||||||
|
IfTypeMfSiglink = 167
|
||||||
|
IfTypeHdsl2 = 168
|
||||||
|
IfTypeShdsl = 169
|
||||||
|
IfTypeDs1Fdl = 170
|
||||||
|
IfTypePos = 171
|
||||||
|
IfTypeDvbAsiIn = 172
|
||||||
|
IfTypeDvbAsiOut = 173
|
||||||
|
IfTypePlc = 174
|
||||||
|
IfTypeNfas = 175
|
||||||
|
IfTypeTr008 = 176
|
||||||
|
IfTypeGr303Rdt = 177
|
||||||
|
IfTypeGr303Idt = 178
|
||||||
|
IfTypeIsup = 179
|
||||||
|
IfTypePropDocsWirelessMaclayer = 180
|
||||||
|
IfTypePropDocsWirelessDownstream = 181
|
||||||
|
IfTypePropDocsWirelessUpstream = 182
|
||||||
|
IfTypeHiperlan2 = 183
|
||||||
|
IfTypePropBwaP2MP = 184
|
||||||
|
IfTypeSonetOverheadChannel = 185
|
||||||
|
IfTypeDigitalWrapperOverheadChannel = 186
|
||||||
|
IfTypeAal2 = 187
|
||||||
|
IfTypeRadioMac = 188
|
||||||
|
IfTypeAtmRadio = 189
|
||||||
|
IfTypeImt = 190
|
||||||
|
IfTypeMvl = 191
|
||||||
|
IfTypeReachDsl = 192
|
||||||
|
IfTypeFrDlciEndpt = 193
|
||||||
|
IfTypeAtmVciEndpt = 194
|
||||||
|
IfTypeOpticalChannel = 195
|
||||||
|
IfTypeOpticalTransport = 196
|
||||||
|
IfTypeIEEE80216Wman = 237
|
||||||
|
IfTypeWwanpp = 243 // WWAN devices based on GSM technology
|
||||||
|
IfTypeWwanpp2 = 244 // WWAN devices based on CDMA technology
|
||||||
|
IfTypeIEEE802154 = 259 // IEEE 802.15.4 WPAN interface
|
||||||
|
IfTypeXboxWireless = 281
|
||||||
|
)
|
||||||
|
|
||||||
|
// MibIfEntryLevel enumeration specifies level of interface information to retrieve in GetIfTable2Ex function call.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2ex
|
||||||
|
type MibIfEntryLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MibIfEntryNormal MibIfEntryLevel = 0
|
||||||
|
MibIfEntryNormalWithoutStatistics = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// NdisMedium enumeration type identifies the medium types that NDIS drivers support.
|
||||||
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddndis/ne-ntddndis-_ndis_medium
|
||||||
|
type NdisMedium uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NdisMedium802_3 NdisMedium = iota
|
||||||
|
NdisMedium802_5
|
||||||
|
NdisMediumFddi
|
||||||
|
NdisMediumWan
|
||||||
|
NdisMediumLocalTalk
|
||||||
|
NdisMediumDix // defined for convenience, not a real medium
|
||||||
|
NdisMediumArcnetRaw
|
||||||
|
NdisMediumArcnet878_2
|
||||||
|
NdisMediumAtm
|
||||||
|
NdisMediumWirelessWan
|
||||||
|
NdisMediumIrda
|
||||||
|
NdisMediumBpc
|
||||||
|
NdisMediumCoWan
|
||||||
|
NdisMedium1394
|
||||||
|
NdisMediumInfiniBand
|
||||||
|
NdisMediumTunnel
|
||||||
|
NdisMediumNative802_11
|
||||||
|
NdisMediumLoopback
|
||||||
|
NdisMediumWiMAX
|
||||||
|
NdisMediumIP
|
||||||
|
NdisMediumMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// NdisPhysicalMedium describes NDIS physical medium type.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_row2
|
||||||
|
type NdisPhysicalMedium uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NdisPhysicalMediumUnspecified NdisPhysicalMedium = iota
|
||||||
|
NdisPhysicalMediumWirelessLan
|
||||||
|
NdisPhysicalMediumCableModem
|
||||||
|
NdisPhysicalMediumPhoneLine
|
||||||
|
NdisPhysicalMediumPowerLine
|
||||||
|
NdisPhysicalMediumDSL // includes ADSL and UADSL (G.Lite)
|
||||||
|
NdisPhysicalMediumFibreChannel
|
||||||
|
NdisPhysicalMedium1394
|
||||||
|
NdisPhysicalMediumWirelessWan
|
||||||
|
NdisPhysicalMediumNative802_11
|
||||||
|
NdisPhysicalMediumBluetooth
|
||||||
|
NdisPhysicalMediumInfiniband
|
||||||
|
NdisPhysicalMediumWiMax
|
||||||
|
NdisPhysicalMediumUWB
|
||||||
|
NdisPhysicalMedium802_3
|
||||||
|
NdisPhysicalMedium802_5
|
||||||
|
NdisPhysicalMediumIrda
|
||||||
|
NdisPhysicalMediumWiredWAN
|
||||||
|
NdisPhysicalMediumWiredCoWan
|
||||||
|
NdisPhysicalMediumOther
|
||||||
|
NdisPhysicalMediumNative802_15_4
|
||||||
|
NdisPhysicalMediumMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetIfAccessType enumeration type specifies the NDIS network interface access type.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-_net_if_access_type
|
||||||
|
type NetIfAccessType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NetIfAccessLoopback NetIfAccessType = iota + 1
|
||||||
|
NetIfAccessBroadcast
|
||||||
|
NetIfAccessPointToPoint
|
||||||
|
NetIfAccessPointToMultiPoint
|
||||||
|
NetIfAccessMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetIfAdminStatus enumeration type specifies the NDIS network interface administrative status, as described in RFC 2863.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-net_if_admin_status
|
||||||
|
type NetIfAdminStatus uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NetIfAdminStatusUp NetIfAdminStatus = iota + 1
|
||||||
|
NetIfAdminStatusDown
|
||||||
|
NetIfAdminStatusTesting
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetIfConnectionType enumeration type specifies the NDIS network interface connection type.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-_net_if_connection_type
|
||||||
|
type NetIfConnectionType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NetIfConnectionDedicated NetIfConnectionType = iota + 1
|
||||||
|
NetIfConnectionPassive
|
||||||
|
NetIfConnectionDemand
|
||||||
|
NetIfConnectionMaximum
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetIfDirectionType enumeration type specifies the NDIS network interface direction type.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-net_if_direction_type
|
||||||
|
type NetIfDirectionType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NetIfDirectionSendReceive NetIfDirectionType = iota
|
||||||
|
NetIfDirectionSendOnly
|
||||||
|
NetIfDirectionReceiveOnly
|
||||||
|
NetIfDirectionMaximum
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetIfMediaConnectState enumeration type specifies the NDIS network interface connection state.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-_net_if_media_connect_state
|
||||||
|
type NetIfMediaConnectState uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MediaConnectStateUnknown NetIfMediaConnectState = iota
|
||||||
|
MediaConnectStateConnected
|
||||||
|
MediaConnectStateDisconnected
|
||||||
|
)
|
||||||
|
|
||||||
|
// DadState enumeration specifies information about the duplicate address detection (DAD) state for an IPv4 or IPv6 address.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_dad_state
|
||||||
|
type DadState uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
DadStateInvalid DadState = iota
|
||||||
|
DadStateTentative
|
||||||
|
DadStateDuplicate
|
||||||
|
DadStateDeprecated
|
||||||
|
DadStatePreferred
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrefixOrigin enumeration specifies the origin of an IPv4 or IPv6 address prefix, and is used with the IP_ADAPTER_UNICAST_ADDRESS structure.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_prefix_origin
|
||||||
|
type PrefixOrigin uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrefixOriginOther PrefixOrigin = iota
|
||||||
|
PrefixOriginManual
|
||||||
|
PrefixOriginWellKnown
|
||||||
|
PrefixOriginDHCP
|
||||||
|
PrefixOriginRouterAdvertisement
|
||||||
|
PrefixOriginUnchanged = 1 << 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinkLocalAddressBehavior enumeration type defines the link local address behavior.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-_nl_link_local_address_behavior
|
||||||
|
type LinkLocalAddressBehavior int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
LinkLocalAddressAlwaysOff LinkLocalAddressBehavior = iota // Never use link locals.
|
||||||
|
LinkLocalAddressDelayed // Use link locals only if no other addresses. (default for IPv4). Legacy mapping: IPAutoconfigurationEnabled.
|
||||||
|
LinkLocalAddressAlwaysOn // Always use link locals (default for IPv6).
|
||||||
|
LinkLocalAddressUnchanged = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
// OffloadRod enumeration specifies a set of flags that indicate the offload capabilities for an IP interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ns-nldef-_nl_interface_offload_rod
|
||||||
|
type OffloadRod uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChecksumSupported OffloadRod = 1 << iota
|
||||||
|
OptionsSupported
|
||||||
|
DatagramChecksumSupported
|
||||||
|
StreamChecksumSupported
|
||||||
|
StreamOptionsSupported
|
||||||
|
FastPathCompatible
|
||||||
|
LargeSendOffloadSupported
|
||||||
|
GiantSendOffloadSupported
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouteOrigin enumeration type defines the origin of the IP route.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_route_origin
|
||||||
|
type RouteOrigin uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RouteOriginManual RouteOrigin = iota
|
||||||
|
RouteOriginWellKnown
|
||||||
|
RouteOriginDHCP
|
||||||
|
RouteOriginRouterAdvertisement
|
||||||
|
RouteOrigin6to4
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouteProtocol enumeration type defines the routing mechanism that an IP route was added with, as described in RFC 4292.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_route_protocol
|
||||||
|
type RouteProtocol uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RouteProtocolOther RouteProtocol = iota + 1
|
||||||
|
RouteProtocolLocal
|
||||||
|
RouteProtocolNetMgmt
|
||||||
|
RouteProtocolIcmp
|
||||||
|
RouteProtocolEgp
|
||||||
|
RouteProtocolGgp
|
||||||
|
RouteProtocolHello
|
||||||
|
RouteProtocolRip
|
||||||
|
RouteProtocolIsIs
|
||||||
|
RouteProtocolEsIs
|
||||||
|
RouteProtocolCisco
|
||||||
|
RouteProtocolBbn
|
||||||
|
RouteProtocolOspf
|
||||||
|
RouteProtocolBgp
|
||||||
|
RouteProtocolIdpr
|
||||||
|
RouteProtocolEigrp
|
||||||
|
RouteProtocolDvmrp
|
||||||
|
RouteProtocolRpl
|
||||||
|
RouteProtocolDHCP
|
||||||
|
RouteProtocolNTAutostatic = 10002
|
||||||
|
RouteProtocolNTStatic = 10006
|
||||||
|
RouteProtocolNTStaticNonDOD = 10007
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouterDiscoveryBehavior enumeration type defines the router discovery behavior, as described in RFC 2461.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-_nl_router_discovery_behavior
|
||||||
|
type RouterDiscoveryBehavior int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RouterDiscoveryDisabled RouterDiscoveryBehavior = iota
|
||||||
|
RouterDiscoveryEnabled
|
||||||
|
RouterDiscoveryDHCP
|
||||||
|
RouterDiscoveryUnchanged = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
// SuffixOrigin enumeration specifies the origin of an IPv4 or IPv6 address suffix, and is used with the IP_ADAPTER_UNICAST_ADDRESS structure.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/nldef/ne-nldef-nl_suffix_origin
|
||||||
|
type SuffixOrigin uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
SuffixOriginOther SuffixOrigin = iota
|
||||||
|
SuffixOriginManual
|
||||||
|
SuffixOriginWellKnown
|
||||||
|
SuffixOriginDHCP
|
||||||
|
SuffixOriginLinkLayerAddress
|
||||||
|
SuffixOriginRandom
|
||||||
|
SuffixOriginUnchanged = 1 << 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// MibNotificationType enumeration defines the notification type passed to a callback function when a notification occurs.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ne-netioapi-_mib_notification_type
|
||||||
|
type MibNotificationType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MibParameterNotification MibNotificationType = iota // Parameter change
|
||||||
|
MibAddInstance // Addition
|
||||||
|
MibDeleteInstance // Deletion
|
||||||
|
MibInitialNotification // Initial notification
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChangeCallback interface {
|
||||||
|
Unregister() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TunnelType enumeration type defines the encapsulation method used by a tunnel, as described by the Internet Assigned Names Authority (IANA).
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ifdef/ne-ifdef-tunnel_type
|
||||||
|
type TunnelType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
TunnelTypeNone TunnelType = 0
|
||||||
|
TunnelTypeOther = 1
|
||||||
|
TunnelTypeDirect = 2
|
||||||
|
TunnelType6to4 = 11
|
||||||
|
TunnelTypeIsatap = 13
|
||||||
|
TunnelTypeTeredo = 14
|
||||||
|
TunnelTypeIPHTTPS = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
// InterfaceAndOperStatusFlags enumeration type defines interface and operation flags
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_row2
|
||||||
|
type InterfaceAndOperStatusFlags uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
IAOSFHardwareInterface InterfaceAndOperStatusFlags = 1 << iota
|
||||||
|
IAOSFFilterInterface
|
||||||
|
IAOSFConnectorPresent
|
||||||
|
IAOSFNotAuthenticated
|
||||||
|
IAOSFNotMediaConnected
|
||||||
|
IAOSFPaused
|
||||||
|
IAOSFLowPower
|
||||||
|
IAOSFEndPointInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
// GAAFlags enumeration defines flags used in GetAdaptersAddresses calls
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
|
||||||
|
type GAAFlags uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
GAAFlagSkipUnicast GAAFlags = 1 << iota
|
||||||
|
GAAFlagSkipAnycast
|
||||||
|
GAAFlagSkipMulticast
|
||||||
|
GAAFlagSkipDNSServer
|
||||||
|
GAAFlagIncludePrefix
|
||||||
|
GAAFlagSkipFriendlyName
|
||||||
|
GAAFlagIncludeWinsInfo
|
||||||
|
GAAFlagIncludeGateways
|
||||||
|
GAAFlagIncludeAllInterfaces
|
||||||
|
GAAFlagIncludeAllCompartments
|
||||||
|
GAAFlagIncludeTunnelBindingOrder
|
||||||
|
GAAFlagSkipDNSInfo
|
||||||
|
|
||||||
|
GAAFlagDefault GAAFlags = 0
|
||||||
|
GAAFlagSkipAll = GAAFlagSkipUnicast | GAAFlagSkipAnycast | GAAFlagSkipMulticast | GAAFlagSkipDNSServer | GAAFlagSkipFriendlyName | GAAFlagSkipDNSInfo
|
||||||
|
GAAFlagIncludeAll = GAAFlagIncludePrefix | GAAFlagIncludeWinsInfo | GAAFlagIncludeGateways | GAAFlagIncludeAllInterfaces | GAAFlagIncludeAllCompartments | GAAFlagIncludeTunnelBindingOrder
|
||||||
|
)
|
||||||
|
|
||||||
|
// ScopeLevel enumeration is used with the IP_ADAPTER_ADDRESSES structure to identify scope levels for IPv6 addresses.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ws2def/ne-ws2def-scope_level
|
||||||
|
type ScopeLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScopeLevelInterface ScopeLevel = 1
|
||||||
|
ScopeLevelLink = 2
|
||||||
|
ScopeLevelSubnet = 3
|
||||||
|
ScopeLevelAdmin = 4
|
||||||
|
ScopeLevelSite = 5
|
||||||
|
ScopeLevelOrganization = 8
|
||||||
|
ScopeLevelGlobal = 14
|
||||||
|
ScopeLevelCount = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouteData structure describes a route to add
|
||||||
|
type RouteData struct {
|
||||||
|
Destination net.IPNet
|
||||||
|
NextHop net.IP
|
||||||
|
Metric uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAdapterDNSSuffix structure stores a DNS suffix in a linked list of DNS suffixes for a particular adapter.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_dns_suffix
|
||||||
|
type IPAdapterDNSSuffix struct {
|
||||||
|
Next *IPAdapterDNSSuffix
|
||||||
|
str [maxDNSSuffixStringLength]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// String method returns the DNS suffix for this DNS suffix entry.
|
||||||
|
func (obj *IPAdapterDNSSuffix) String() string {
|
||||||
|
return windows.UTF16ToString(obj.str[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdapterName method returns the name of the adapter with which these addresses are associated.
|
||||||
|
// Unlike an adapter's friendly name, the adapter name returned by AdapterName is permanent and cannot be modified by the user.
|
||||||
|
func (addr *IPAdapterAddresses) AdapterName() string {
|
||||||
|
return windows.BytePtrToString(addr.adapterName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSSuffix method returns adapter DNS suffix associated with this adapter.
|
||||||
|
func (addr *IPAdapterAddresses) DNSSuffix() string {
|
||||||
|
if addr.dnsSuffix == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return windows.UTF16PtrToString(addr.dnsSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description method returns description for the adapter.
|
||||||
|
func (addr *IPAdapterAddresses) Description() string {
|
||||||
|
if addr.description == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return windows.UTF16PtrToString(addr.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FriendlyName method returns a user-friendly name for the adapter. For example: "Local Area Connection 1."
|
||||||
|
// This name appears in contexts such as the ipconfig command line program and the Connection folder.
|
||||||
|
func (addr *IPAdapterAddresses) FriendlyName() string {
|
||||||
|
if addr.friendlyName == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return windows.UTF16PtrToString(addr.friendlyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PhysicalAddress method returns the Media Access Control (MAC) address for the adapter.
|
||||||
|
// For example, on an Ethernet network this member would specify the Ethernet hardware address.
|
||||||
|
func (addr *IPAdapterAddresses) PhysicalAddress() []byte {
|
||||||
|
return addr.physicalAddress[:addr.physicalAddressLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHCPv6ClientDUID method returns the DHCP unique identifier (DUID) for the DHCPv6 client.
|
||||||
|
// This information is only applicable to an IPv6 adapter address configured using DHCPv6.
|
||||||
|
func (addr *IPAdapterAddresses) DHCPv6ClientDUID() []byte {
|
||||||
|
return addr.dhcpv6ClientDUID[:addr.dhcpv6ClientDUIDLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init method initializes the members of an MIB_IPINTERFACE_ROW entry with default values.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-initializeipinterfaceentry
|
||||||
|
func (row *MibIPInterfaceRow) Init() {
|
||||||
|
initializeIPInterfaceEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method retrieves IP information for the specified interface on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipinterfaceentry
|
||||||
|
func (row *MibIPInterfaceRow) get() error {
|
||||||
|
if err := getIPInterfaceEntry(row); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch that fixes SitePrefixLength issue
|
||||||
|
// https://stackoverflow.com/questions/54857292/setipinterfaceentry-returns-error-invalid-parameter?noredirect=1
|
||||||
|
switch row.Family {
|
||||||
|
case windows.AF_INET:
|
||||||
|
if row.SitePrefixLength > 32 {
|
||||||
|
row.SitePrefixLength = 0
|
||||||
|
}
|
||||||
|
case windows.AF_INET6:
|
||||||
|
if row.SitePrefixLength > 128 {
|
||||||
|
row.SitePrefixLength = 128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set method sets the properties of an IP interface on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-setipinterfaceentry
|
||||||
|
func (row *MibIPInterfaceRow) Set() error {
|
||||||
|
return setIPInterfaceEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method returns all table rows as a Go slice.
|
||||||
|
func (tab *mibIPInterfaceTable) get() (s []MibIPInterfaceRow) {
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), unsafe.Pointer(&tab.table[0]), int(tab.numEntries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// free method frees the buffer allocated by the functions that return tables of network interfaces, addresses, and routes.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-freemibtable
|
||||||
|
func (tab *mibIPInterfaceTable) free() {
|
||||||
|
freeMibTable(unsafe.Pointer(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias method returns a string that contains the alias name of the network interface.
|
||||||
|
func (row *MibIfRow2) Alias() string {
|
||||||
|
return windows.UTF16ToString(row.alias[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description method returns a string that contains a description of the network interface.
|
||||||
|
func (row *MibIfRow2) Description() string {
|
||||||
|
return windows.UTF16ToString(row.description[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PhysicalAddress method returns the physical hardware address of the adapter for this network interface.
|
||||||
|
func (row *MibIfRow2) PhysicalAddress() []byte {
|
||||||
|
return row.physicalAddress[:row.physicalAddressLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermanentPhysicalAddress method returns the permanent physical hardware address of the adapter for this network interface.
|
||||||
|
func (row *MibIfRow2) PermanentPhysicalAddress() []byte {
|
||||||
|
return row.permanentPhysicalAddress[:row.physicalAddressLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method retrieves information for the specified interface on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2
|
||||||
|
func (row *MibIfRow2) get() (ret error) {
|
||||||
|
return getIfEntry2(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method returns all table rows as a Go slice.
|
||||||
|
func (tab *mibIfTable2) get() (s []MibIfRow2) {
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), unsafe.Pointer(&tab.table[0]), int(tab.numEntries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// free method frees the buffer allocated by the functions that return tables of network interfaces, addresses, and routes.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-freemibtable
|
||||||
|
func (tab *mibIfTable2) free() {
|
||||||
|
freeMibTable(unsafe.Pointer(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawSockaddrInet union contains an IPv4, an IPv6 address, or an address family.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/ws2ipdef/ns-ws2ipdef-_sockaddr_inet
|
||||||
|
type RawSockaddrInet struct {
|
||||||
|
Family AddressFamily
|
||||||
|
data [26]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIP method sets family, address, and port to the given IPv4 or IPv6 address and port.
|
||||||
|
// All other members of the structure are set to zero.
|
||||||
|
func (addr *RawSockaddrInet) SetIP(ip net.IP, port uint16) error {
|
||||||
|
if v4 := ip.To4(); v4 != nil {
|
||||||
|
addr4 := (*windows.RawSockaddrInet4)(unsafe.Pointer(addr))
|
||||||
|
addr4.Family = windows.AF_INET
|
||||||
|
copy(addr4.Addr[:], v4)
|
||||||
|
addr4.Port = port
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
addr4.Zero[i] = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v6 := ip.To16(); v6 != nil {
|
||||||
|
addr6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(addr))
|
||||||
|
addr6.Family = windows.AF_INET6
|
||||||
|
addr6.Port = port
|
||||||
|
addr6.Flowinfo = 0
|
||||||
|
copy(addr6.Addr[:], v6)
|
||||||
|
addr6.Scope_id = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows.ERROR_INVALID_PARAMETER
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP method returns IPv4 or IPv6 address.
|
||||||
|
// If the address is neither IPv4 not IPv6 nil is returned.
|
||||||
|
func (addr *RawSockaddrInet) IP() net.IP {
|
||||||
|
switch addr.Family {
|
||||||
|
case windows.AF_INET:
|
||||||
|
return (*windows.RawSockaddrInet4)(unsafe.Pointer(addr)).Addr[:]
|
||||||
|
|
||||||
|
case windows.AF_INET6:
|
||||||
|
return (*windows.RawSockaddrInet6)(unsafe.Pointer(addr)).Addr[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init method initializes a MibUnicastIPAddressRow structure with default values for a unicast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-initializeunicastipaddressentry
|
||||||
|
func (row *MibUnicastIPAddressRow) Init() {
|
||||||
|
initializeUnicastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method retrieves information for an existing unicast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry
|
||||||
|
func (row *MibUnicastIPAddressRow) get() error {
|
||||||
|
return getUnicastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set method sets the properties of an existing unicast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-setunicastipaddressentry
|
||||||
|
func (row *MibUnicastIPAddressRow) Set() error {
|
||||||
|
return setUnicastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create method adds a new unicast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry
|
||||||
|
func (row *MibUnicastIPAddressRow) Create() error {
|
||||||
|
return createUnicastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete method deletes an existing unicast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry
|
||||||
|
func (row *MibUnicastIPAddressRow) Delete() error {
|
||||||
|
return deleteUnicastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method returns all table rows as a Go slice.
|
||||||
|
func (tab *mibUnicastIPAddressTable) get() (s []MibUnicastIPAddressRow) {
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), unsafe.Pointer(&tab.table[0]), int(tab.numEntries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// free method frees the buffer allocated by the functions that return tables of network interfaces, addresses, and routes.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-freemibtable
|
||||||
|
func (tab *mibUnicastIPAddressTable) free() {
|
||||||
|
freeMibTable(unsafe.Pointer(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method retrieves information for an existing anycast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getanycastipaddressentry
|
||||||
|
func (row *MibAnycastIPAddressRow) get() error {
|
||||||
|
return getAnycastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create method adds a new anycast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createanycastipaddressentry
|
||||||
|
func (row *MibAnycastIPAddressRow) Create() error {
|
||||||
|
return createAnycastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete method deletes an existing anycast IP address entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteanycastipaddressentry
|
||||||
|
func (row *MibAnycastIPAddressRow) Delete() error {
|
||||||
|
return deleteAnycastIPAddressEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method returns all table rows as a Go slice.
|
||||||
|
func (tab *mibAnycastIPAddressTable) get() (s []MibAnycastIPAddressRow) {
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), unsafe.Pointer(&tab.table[0]), int(tab.numEntries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// free method frees the buffer allocated by the functions that return tables of network interfaces, addresses, and routes.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-freemibtable
|
||||||
|
func (tab *mibAnycastIPAddressTable) free() {
|
||||||
|
freeMibTable(unsafe.Pointer(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAddressPrefix structure stores an IP address prefix.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_ip_address_prefix
|
||||||
|
type IPAddressPrefix struct {
|
||||||
|
Prefix RawSockaddrInet
|
||||||
|
PrefixLength uint8
|
||||||
|
_ [2]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIPNet method sets IP address prefix using net.IPNet.
|
||||||
|
func (prefix *IPAddressPrefix) SetIPNet(net net.IPNet) error {
|
||||||
|
err := prefix.Prefix.SetIP(net.IP, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ones, _ := net.Mask.Size()
|
||||||
|
prefix.PrefixLength = uint8(ones)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNet method returns IP address prefix as net.IPNet.
|
||||||
|
// If the address is neither IPv4 not IPv6 an empty net.IPNet is returned. The resulting net.IPNet should be checked appropriately.
|
||||||
|
func (prefix *IPAddressPrefix) IPNet() net.IPNet {
|
||||||
|
switch prefix.Prefix.Family {
|
||||||
|
case windows.AF_INET:
|
||||||
|
return net.IPNet{IP: (*windows.RawSockaddrInet4)(unsafe.Pointer(&prefix.Prefix)).Addr[:], Mask: net.CIDRMask(int(prefix.PrefixLength), 8*net.IPv4len)}
|
||||||
|
case windows.AF_INET6:
|
||||||
|
return net.IPNet{IP: (*windows.RawSockaddrInet6)(unsafe.Pointer(&prefix.Prefix)).Addr[:], Mask: net.CIDRMask(int(prefix.PrefixLength), 8*net.IPv6len)}
|
||||||
|
}
|
||||||
|
return net.IPNet{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibIPforwardRow2 structure stores information about an IP route entry.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipforward_row2
|
||||||
|
type MibIPforwardRow2 struct {
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
DestinationPrefix IPAddressPrefix
|
||||||
|
NextHop RawSockaddrInet
|
||||||
|
SitePrefixLength uint8
|
||||||
|
ValidLifetime uint32
|
||||||
|
PreferredLifetime uint32
|
||||||
|
Metric uint32
|
||||||
|
Protocol RouteProtocol
|
||||||
|
Loopback bool
|
||||||
|
AutoconfigureAddress bool
|
||||||
|
Publish bool
|
||||||
|
Immortal bool
|
||||||
|
Age uint32
|
||||||
|
Origin RouteOrigin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init method initializes a MIB_IPFORWARD_ROW2 structure with default values for an IP route entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-initializeipforwardentry
|
||||||
|
func (row *MibIPforwardRow2) Init() {
|
||||||
|
initializeIPForwardEntry(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method retrieves information for an IP route entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2
|
||||||
|
func (row *MibIPforwardRow2) get() error {
|
||||||
|
return getIPForwardEntry2(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set method sets the properties of an IP route entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-setipforwardentry2
|
||||||
|
func (row *MibIPforwardRow2) Set() error {
|
||||||
|
return setIPForwardEntry2(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create method creates a new IP route entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2
|
||||||
|
func (row *MibIPforwardRow2) Create() error {
|
||||||
|
return createIPForwardEntry2(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete method deletes an IP route entry on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2
|
||||||
|
func (row *MibIPforwardRow2) Delete() error {
|
||||||
|
return deleteIPForwardEntry2(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get method returns all table rows as a Go slice.
|
||||||
|
func (tab *mibIPforwardTable2) get() (s []MibIPforwardRow2) {
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), unsafe.Pointer(&tab.table[0]), int(tab.numEntries))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// free method frees the buffer allocated by the functions that return tables of network interfaces, addresses, and routes.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-freemibtable
|
||||||
|
func (tab *mibIPforwardTable2) free() {
|
||||||
|
freeMibTable(unsafe.Pointer(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Undocumented DNS API
|
||||||
|
//
|
||||||
|
|
||||||
|
// dnsInterfaceSettings is mean to be used with setInterfaceDnsSettings
|
||||||
|
type dnsInterfaceSettings struct {
|
||||||
|
Version uint32
|
||||||
|
_ [4]byte
|
||||||
|
Flags uint64
|
||||||
|
Domain *uint16
|
||||||
|
NameServer *uint16
|
||||||
|
SearchList *uint16
|
||||||
|
RegistrationEnabled uint32
|
||||||
|
RegisterAdapterName uint32
|
||||||
|
EnableLLMNR uint32
|
||||||
|
QueryAdapterName uint32
|
||||||
|
ProfileNameServer *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
disVersion1 = 1
|
||||||
|
disVersion2 = 2
|
||||||
|
|
||||||
|
disFlagsIPv6 = 0x1
|
||||||
|
disFlagsNameServer = 0x2
|
||||||
|
disFlagsSearchList = 0x4
|
||||||
|
disFlagsRegistrationEnabled = 0x8
|
||||||
|
disFlagsRegisterAdapterName = 0x10
|
||||||
|
disFlagsDomain = 0x20
|
||||||
|
disFlagsHostname = 0x40 // ??
|
||||||
|
disFlagsEnableLLMNR = 0x80
|
||||||
|
disFlagsQueryAdapterName = 0x100
|
||||||
|
disFlagsProfileNameServer = 0x200
|
||||||
|
disFlagsVersion2 = 0x400 // ?? - v2 only
|
||||||
|
disFlagsMoreFlags = 0x800 // ?? - v2 only
|
||||||
|
)
|
||||||
|
|
||||||
|
// unsafeSlice updates the slice slicePtr to be a slice
|
||||||
|
// referencing the provided data with its length & capacity set to
|
||||||
|
// lenCap.
|
||||||
|
//
|
||||||
|
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||||
|
// update callers to use unsafe.Slice instead of this.
|
||||||
|
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||||
|
type sliceHeader struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
h := (*sliceHeader)(slicePtr)
|
||||||
|
h.Data = data
|
||||||
|
h.Len = lenCap
|
||||||
|
h.Cap = lenCap
|
||||||
|
}
|
227
listener/tun/dev/winipcfg/types_32.go
Normal file
227
listener/tun/dev/winipcfg/types_32.go
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
// +build 386 arm
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPAdapterWINSServerAddress structure stores a single Windows Internet Name Service (WINS) server address in a linked list of WINS server addresses for a particular adapter.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_wins_server_address_lh
|
||||||
|
type IPAdapterWINSServerAddress struct {
|
||||||
|
Length uint32
|
||||||
|
_ uint32
|
||||||
|
Next *IPAdapterWINSServerAddress
|
||||||
|
Address windows.SocketAddress
|
||||||
|
_ [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAdapterGatewayAddress structure stores a single gateway address in a linked list of gateway addresses for a particular adapter.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_gateway_address_lh
|
||||||
|
type IPAdapterGatewayAddress struct {
|
||||||
|
Length uint32
|
||||||
|
_ uint32
|
||||||
|
Next *IPAdapterGatewayAddress
|
||||||
|
Address windows.SocketAddress
|
||||||
|
_ [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAdapterAddresses structure is the header node for a linked list of addresses for a particular adapter. This structure can simultaneously be used as part of a linked list of IP_ADAPTER_ADDRESSES structures.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_addresses_lh
|
||||||
|
// This is a modified and extended version of windows.IpAdapterAddresses.
|
||||||
|
type IPAdapterAddresses struct {
|
||||||
|
Length uint32
|
||||||
|
IfIndex uint32
|
||||||
|
Next *IPAdapterAddresses
|
||||||
|
adapterName *byte
|
||||||
|
FirstUnicastAddress *windows.IpAdapterUnicastAddress
|
||||||
|
FirstAnycastAddress *windows.IpAdapterAnycastAddress
|
||||||
|
FirstMulticastAddress *windows.IpAdapterMulticastAddress
|
||||||
|
FirstDNSServerAddress *windows.IpAdapterDnsServerAdapter
|
||||||
|
dnsSuffix *uint16
|
||||||
|
description *uint16
|
||||||
|
friendlyName *uint16
|
||||||
|
physicalAddress [windows.MAX_ADAPTER_ADDRESS_LENGTH]byte
|
||||||
|
physicalAddressLength uint32
|
||||||
|
Flags IPAAFlags
|
||||||
|
MTU uint32
|
||||||
|
IfType IfType
|
||||||
|
OperStatus IfOperStatus
|
||||||
|
IPv6IfIndex uint32
|
||||||
|
ZoneIndices [16]uint32
|
||||||
|
FirstPrefix *windows.IpAdapterPrefix
|
||||||
|
TransmitLinkSpeed uint64
|
||||||
|
ReceiveLinkSpeed uint64
|
||||||
|
FirstWINSServerAddress *IPAdapterWINSServerAddress
|
||||||
|
FirstGatewayAddress *IPAdapterGatewayAddress
|
||||||
|
Ipv4Metric uint32
|
||||||
|
Ipv6Metric uint32
|
||||||
|
LUID LUID
|
||||||
|
DHCPv4Server windows.SocketAddress
|
||||||
|
CompartmentID uint32
|
||||||
|
NetworkGUID windows.GUID
|
||||||
|
ConnectionType NetIfConnectionType
|
||||||
|
TunnelType TunnelType
|
||||||
|
DHCPv6Server windows.SocketAddress
|
||||||
|
dhcpv6ClientDUID [maxDHCPv6DUIDLength]byte
|
||||||
|
dhcpv6ClientDUIDLength uint32
|
||||||
|
DHCPv6IAID uint32
|
||||||
|
FirstDNSSuffix *IPAdapterDNSSuffix
|
||||||
|
_ [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibIPInterfaceRow structure stores interface management information for a particular IP address family on a network interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipinterface_row
|
||||||
|
type MibIPInterfaceRow struct {
|
||||||
|
Family AddressFamily
|
||||||
|
_ [4]byte
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
MaxReassemblySize uint32
|
||||||
|
InterfaceIdentifier uint64
|
||||||
|
MinRouterAdvertisementInterval uint32
|
||||||
|
MaxRouterAdvertisementInterval uint32
|
||||||
|
AdvertisingEnabled bool
|
||||||
|
ForwardingEnabled bool
|
||||||
|
WeakHostSend bool
|
||||||
|
WeakHostReceive bool
|
||||||
|
UseAutomaticMetric bool
|
||||||
|
UseNeighborUnreachabilityDetection bool
|
||||||
|
ManagedAddressConfigurationSupported bool
|
||||||
|
OtherStatefulConfigurationSupported bool
|
||||||
|
AdvertiseDefaultRoute bool
|
||||||
|
RouterDiscoveryBehavior RouterDiscoveryBehavior
|
||||||
|
DadTransmits uint32
|
||||||
|
BaseReachableTime uint32
|
||||||
|
RetransmitTime uint32
|
||||||
|
PathMTUDiscoveryTimeout uint32
|
||||||
|
LinkLocalAddressBehavior LinkLocalAddressBehavior
|
||||||
|
LinkLocalAddressTimeout uint32
|
||||||
|
ZoneIndices [ScopeLevelCount]uint32
|
||||||
|
SitePrefixLength uint32
|
||||||
|
Metric uint32
|
||||||
|
NLMTU uint32
|
||||||
|
Connected bool
|
||||||
|
SupportsWakeUpPatterns bool
|
||||||
|
SupportsNeighborDiscovery bool
|
||||||
|
SupportsRouterDiscovery bool
|
||||||
|
ReachableTime uint32
|
||||||
|
TransmitOffload OffloadRod
|
||||||
|
ReceiveOffload OffloadRod
|
||||||
|
DisableDefaultRoutes bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIPInterfaceTable structure contains a table of IP interface entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipinterface_table
|
||||||
|
type mibIPInterfaceTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
_ [4]byte
|
||||||
|
table [anySize]MibIPInterfaceRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibIfRow2 structure stores information about a particular interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_row2
|
||||||
|
type MibIfRow2 struct {
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
InterfaceGUID windows.GUID
|
||||||
|
alias [ifMaxStringSize + 1]uint16
|
||||||
|
description [ifMaxStringSize + 1]uint16
|
||||||
|
physicalAddressLength uint32
|
||||||
|
physicalAddress [ifMaxPhysAddressLength]byte
|
||||||
|
permanentPhysicalAddress [ifMaxPhysAddressLength]byte
|
||||||
|
MTU uint32
|
||||||
|
Type IfType
|
||||||
|
TunnelType TunnelType
|
||||||
|
MediaType NdisMedium
|
||||||
|
PhysicalMediumType NdisPhysicalMedium
|
||||||
|
AccessType NetIfAccessType
|
||||||
|
DirectionType NetIfDirectionType
|
||||||
|
InterfaceAndOperStatusFlags InterfaceAndOperStatusFlags
|
||||||
|
OperStatus IfOperStatus
|
||||||
|
AdminStatus NetIfAdminStatus
|
||||||
|
MediaConnectState NetIfMediaConnectState
|
||||||
|
NetworkGUID windows.GUID
|
||||||
|
ConnectionType NetIfConnectionType
|
||||||
|
_ [4]byte
|
||||||
|
TransmitLinkSpeed uint64
|
||||||
|
ReceiveLinkSpeed uint64
|
||||||
|
InOctets uint64
|
||||||
|
InUcastPkts uint64
|
||||||
|
InNUcastPkts uint64
|
||||||
|
InDiscards uint64
|
||||||
|
InErrors uint64
|
||||||
|
InUnknownProtos uint64
|
||||||
|
InUcastOctets uint64
|
||||||
|
InMulticastOctets uint64
|
||||||
|
InBroadcastOctets uint64
|
||||||
|
OutOctets uint64
|
||||||
|
OutUcastPkts uint64
|
||||||
|
OutNUcastPkts uint64
|
||||||
|
OutDiscards uint64
|
||||||
|
OutErrors uint64
|
||||||
|
OutUcastOctets uint64
|
||||||
|
OutMulticastOctets uint64
|
||||||
|
OutBroadcastOctets uint64
|
||||||
|
OutQLen uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIfTable2 structure contains a table of logical and physical interface entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_table2
|
||||||
|
type mibIfTable2 struct {
|
||||||
|
numEntries uint32
|
||||||
|
_ [4]byte
|
||||||
|
table [anySize]MibIfRow2
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibUnicastIPAddressRow structure stores information about a unicast IP address.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_unicastipaddress_row
|
||||||
|
type MibUnicastIPAddressRow struct {
|
||||||
|
Address RawSockaddrInet
|
||||||
|
_ [4]byte
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
PrefixOrigin PrefixOrigin
|
||||||
|
SuffixOrigin SuffixOrigin
|
||||||
|
ValidLifetime uint32
|
||||||
|
PreferredLifetime uint32
|
||||||
|
OnLinkPrefixLength uint8
|
||||||
|
SkipAsSource bool
|
||||||
|
DadState DadState
|
||||||
|
ScopeID uint32
|
||||||
|
CreationTimeStamp int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibUnicastIPAddressTable structure contains a table of unicast IP address entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_unicastipaddress_table
|
||||||
|
type mibUnicastIPAddressTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
_ [4]byte
|
||||||
|
table [anySize]MibUnicastIPAddressRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibAnycastIPAddressRow structure stores information about an anycast IP address.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_anycastipaddress_row
|
||||||
|
type MibAnycastIPAddressRow struct {
|
||||||
|
Address RawSockaddrInet
|
||||||
|
_ [4]byte
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
ScopeID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibAnycastIPAddressTable structure contains a table of anycast IP address entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-mib_anycastipaddress_table
|
||||||
|
type mibAnycastIPAddressTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
_ [4]byte
|
||||||
|
table [anySize]MibAnycastIPAddressRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIPforwardTable2 structure contains a table of IP route entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipforward_table2
|
||||||
|
type mibIPforwardTable2 struct {
|
||||||
|
numEntries uint32
|
||||||
|
_ [4]byte
|
||||||
|
table [anySize]MibIPforwardRow2
|
||||||
|
}
|
216
listener/tun/dev/winipcfg/types_64.go
Normal file
216
listener/tun/dev/winipcfg/types_64.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
// +build windows
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPAdapterWINSServerAddress structure stores a single Windows Internet Name Service (WINS) server address in a linked list of WINS server addresses for a particular adapter.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_wins_server_address_lh
|
||||||
|
type IPAdapterWINSServerAddress struct {
|
||||||
|
Length uint32
|
||||||
|
_ uint32
|
||||||
|
Next *IPAdapterWINSServerAddress
|
||||||
|
Address windows.SocketAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAdapterGatewayAddress structure stores a single gateway address in a linked list of gateway addresses for a particular adapter.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_gateway_address_lh
|
||||||
|
type IPAdapterGatewayAddress struct {
|
||||||
|
Length uint32
|
||||||
|
_ uint32
|
||||||
|
Next *IPAdapterGatewayAddress
|
||||||
|
Address windows.SocketAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAdapterAddresses structure is the header node for a linked list of addresses for a particular adapter. This structure can simultaneously be used as part of a linked list of IP_ADAPTER_ADDRESSES structures.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_addresses_lh
|
||||||
|
// This is a modified and extended version of windows.IpAdapterAddresses.
|
||||||
|
type IPAdapterAddresses struct {
|
||||||
|
Length uint32
|
||||||
|
IfIndex uint32
|
||||||
|
Next *IPAdapterAddresses
|
||||||
|
adapterName *byte
|
||||||
|
FirstUnicastAddress *windows.IpAdapterUnicastAddress
|
||||||
|
FirstAnycastAddress *windows.IpAdapterAnycastAddress
|
||||||
|
FirstMulticastAddress *windows.IpAdapterMulticastAddress
|
||||||
|
FirstDNSServerAddress *windows.IpAdapterDnsServerAdapter
|
||||||
|
dnsSuffix *uint16
|
||||||
|
description *uint16
|
||||||
|
friendlyName *uint16
|
||||||
|
physicalAddress [windows.MAX_ADAPTER_ADDRESS_LENGTH]byte
|
||||||
|
physicalAddressLength uint32
|
||||||
|
Flags IPAAFlags
|
||||||
|
MTU uint32
|
||||||
|
IfType IfType
|
||||||
|
OperStatus IfOperStatus
|
||||||
|
IPv6IfIndex uint32
|
||||||
|
ZoneIndices [16]uint32
|
||||||
|
FirstPrefix *windows.IpAdapterPrefix
|
||||||
|
TransmitLinkSpeed uint64
|
||||||
|
ReceiveLinkSpeed uint64
|
||||||
|
FirstWINSServerAddress *IPAdapterWINSServerAddress
|
||||||
|
FirstGatewayAddress *IPAdapterGatewayAddress
|
||||||
|
Ipv4Metric uint32
|
||||||
|
Ipv6Metric uint32
|
||||||
|
LUID LUID
|
||||||
|
DHCPv4Server windows.SocketAddress
|
||||||
|
CompartmentID uint32
|
||||||
|
NetworkGUID windows.GUID
|
||||||
|
ConnectionType NetIfConnectionType
|
||||||
|
TunnelType TunnelType
|
||||||
|
DHCPv6Server windows.SocketAddress
|
||||||
|
dhcpv6ClientDUID [maxDHCPv6DUIDLength]byte
|
||||||
|
dhcpv6ClientDUIDLength uint32
|
||||||
|
DHCPv6IAID uint32
|
||||||
|
FirstDNSSuffix *IPAdapterDNSSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibIPInterfaceRow structure stores interface management information for a particular IP address family on a network interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipinterface_row
|
||||||
|
type MibIPInterfaceRow struct {
|
||||||
|
Family AddressFamily
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
MaxReassemblySize uint32
|
||||||
|
InterfaceIdentifier uint64
|
||||||
|
MinRouterAdvertisementInterval uint32
|
||||||
|
MaxRouterAdvertisementInterval uint32
|
||||||
|
AdvertisingEnabled bool
|
||||||
|
ForwardingEnabled bool
|
||||||
|
WeakHostSend bool
|
||||||
|
WeakHostReceive bool
|
||||||
|
UseAutomaticMetric bool
|
||||||
|
UseNeighborUnreachabilityDetection bool
|
||||||
|
ManagedAddressConfigurationSupported bool
|
||||||
|
OtherStatefulConfigurationSupported bool
|
||||||
|
AdvertiseDefaultRoute bool
|
||||||
|
RouterDiscoveryBehavior RouterDiscoveryBehavior
|
||||||
|
DadTransmits uint32
|
||||||
|
BaseReachableTime uint32
|
||||||
|
RetransmitTime uint32
|
||||||
|
PathMTUDiscoveryTimeout uint32
|
||||||
|
LinkLocalAddressBehavior LinkLocalAddressBehavior
|
||||||
|
LinkLocalAddressTimeout uint32
|
||||||
|
ZoneIndices [ScopeLevelCount]uint32
|
||||||
|
SitePrefixLength uint32
|
||||||
|
Metric uint32
|
||||||
|
NLMTU uint32
|
||||||
|
Connected bool
|
||||||
|
SupportsWakeUpPatterns bool
|
||||||
|
SupportsNeighborDiscovery bool
|
||||||
|
SupportsRouterDiscovery bool
|
||||||
|
ReachableTime uint32
|
||||||
|
TransmitOffload OffloadRod
|
||||||
|
ReceiveOffload OffloadRod
|
||||||
|
DisableDefaultRoutes bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIPInterfaceTable structure contains a table of IP interface entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipinterface_table
|
||||||
|
type mibIPInterfaceTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
table [anySize]MibIPInterfaceRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibIfRow2 structure stores information about a particular interface.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_row2
|
||||||
|
type MibIfRow2 struct {
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
InterfaceGUID windows.GUID
|
||||||
|
alias [ifMaxStringSize + 1]uint16
|
||||||
|
description [ifMaxStringSize + 1]uint16
|
||||||
|
physicalAddressLength uint32
|
||||||
|
physicalAddress [ifMaxPhysAddressLength]byte
|
||||||
|
permanentPhysicalAddress [ifMaxPhysAddressLength]byte
|
||||||
|
MTU uint32
|
||||||
|
Type IfType
|
||||||
|
TunnelType TunnelType
|
||||||
|
MediaType NdisMedium
|
||||||
|
PhysicalMediumType NdisPhysicalMedium
|
||||||
|
AccessType NetIfAccessType
|
||||||
|
DirectionType NetIfDirectionType
|
||||||
|
InterfaceAndOperStatusFlags InterfaceAndOperStatusFlags
|
||||||
|
OperStatus IfOperStatus
|
||||||
|
AdminStatus NetIfAdminStatus
|
||||||
|
MediaConnectState NetIfMediaConnectState
|
||||||
|
NetworkGUID windows.GUID
|
||||||
|
ConnectionType NetIfConnectionType
|
||||||
|
TransmitLinkSpeed uint64
|
||||||
|
ReceiveLinkSpeed uint64
|
||||||
|
InOctets uint64
|
||||||
|
InUcastPkts uint64
|
||||||
|
InNUcastPkts uint64
|
||||||
|
InDiscards uint64
|
||||||
|
InErrors uint64
|
||||||
|
InUnknownProtos uint64
|
||||||
|
InUcastOctets uint64
|
||||||
|
InMulticastOctets uint64
|
||||||
|
InBroadcastOctets uint64
|
||||||
|
OutOctets uint64
|
||||||
|
OutUcastPkts uint64
|
||||||
|
OutNUcastPkts uint64
|
||||||
|
OutDiscards uint64
|
||||||
|
OutErrors uint64
|
||||||
|
OutUcastOctets uint64
|
||||||
|
OutMulticastOctets uint64
|
||||||
|
OutBroadcastOctets uint64
|
||||||
|
OutQLen uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIfTable2 structure contains a table of logical and physical interface entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_if_table2
|
||||||
|
type mibIfTable2 struct {
|
||||||
|
numEntries uint32
|
||||||
|
table [anySize]MibIfRow2
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibUnicastIPAddressRow structure stores information about a unicast IP address.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_unicastipaddress_row
|
||||||
|
type MibUnicastIPAddressRow struct {
|
||||||
|
Address RawSockaddrInet
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
PrefixOrigin PrefixOrigin
|
||||||
|
SuffixOrigin SuffixOrigin
|
||||||
|
ValidLifetime uint32
|
||||||
|
PreferredLifetime uint32
|
||||||
|
OnLinkPrefixLength uint8
|
||||||
|
SkipAsSource bool
|
||||||
|
DadState DadState
|
||||||
|
ScopeID uint32
|
||||||
|
CreationTimeStamp int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibUnicastIPAddressTable structure contains a table of unicast IP address entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_unicastipaddress_table
|
||||||
|
type mibUnicastIPAddressTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
table [anySize]MibUnicastIPAddressRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// MibAnycastIPAddressRow structure stores information about an anycast IP address.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_anycastipaddress_row
|
||||||
|
type MibAnycastIPAddressRow struct {
|
||||||
|
Address RawSockaddrInet
|
||||||
|
InterfaceLUID LUID
|
||||||
|
InterfaceIndex uint32
|
||||||
|
ScopeID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibAnycastIPAddressTable structure contains a table of anycast IP address entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-mib_anycastipaddress_table
|
||||||
|
type mibAnycastIPAddressTable struct {
|
||||||
|
numEntries uint32
|
||||||
|
table [anySize]MibAnycastIPAddressRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// mibIPforwardTable2 structure contains a table of IP route entries.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/ns-netioapi-_mib_ipforward_table2
|
||||||
|
type mibIPforwardTable2 struct {
|
||||||
|
numEntries uint32
|
||||||
|
table [anySize]MibIPforwardRow2
|
||||||
|
}
|
85
listener/tun/dev/winipcfg/unicast_address_change_handler.go
Normal file
85
listener/tun/dev/winipcfg/unicast_address_change_handler.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnicastAddressChangeCallback structure allows unicast address change callback handling.
|
||||||
|
type UnicastAddressChangeCallback struct {
|
||||||
|
cb func(notificationType MibNotificationType, unicastAddress *MibUnicastIPAddressRow)
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
unicastAddressChangeAddRemoveMutex = sync.Mutex{}
|
||||||
|
unicastAddressChangeMutex = sync.Mutex{}
|
||||||
|
unicastAddressChangeCallbacks = make(map[*UnicastAddressChangeCallback]bool)
|
||||||
|
unicastAddressChangeHandle = windows.Handle(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterUnicastAddressChangeCallback registers a new UnicastAddressChangeCallback. If this particular callback is already
|
||||||
|
// registered, the function will silently return. Returned UnicastAddressChangeCallback.Unregister method should be used
|
||||||
|
// to unregister.
|
||||||
|
func RegisterUnicastAddressChangeCallback(callback func(notificationType MibNotificationType, unicastAddress *MibUnicastIPAddressRow)) (*UnicastAddressChangeCallback, error) {
|
||||||
|
s := &UnicastAddressChangeCallback{cb: callback}
|
||||||
|
|
||||||
|
unicastAddressChangeAddRemoveMutex.Lock()
|
||||||
|
defer unicastAddressChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
unicastAddressChangeMutex.Lock()
|
||||||
|
defer unicastAddressChangeMutex.Unlock()
|
||||||
|
|
||||||
|
unicastAddressChangeCallbacks[s] = true
|
||||||
|
|
||||||
|
if unicastAddressChangeHandle == 0 {
|
||||||
|
err := notifyUnicastIPAddressChange(windows.AF_UNSPEC, windows.NewCallback(unicastAddressChanged), 0, false, &unicastAddressChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
delete(unicastAddressChangeCallbacks, s)
|
||||||
|
unicastAddressChangeHandle = 0
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister unregisters the callback.
|
||||||
|
func (callback *UnicastAddressChangeCallback) Unregister() error {
|
||||||
|
unicastAddressChangeAddRemoveMutex.Lock()
|
||||||
|
defer unicastAddressChangeAddRemoveMutex.Unlock()
|
||||||
|
|
||||||
|
unicastAddressChangeMutex.Lock()
|
||||||
|
delete(unicastAddressChangeCallbacks, callback)
|
||||||
|
removeIt := len(unicastAddressChangeCallbacks) == 0 && unicastAddressChangeHandle != 0
|
||||||
|
unicastAddressChangeMutex.Unlock()
|
||||||
|
|
||||||
|
callback.wait.Wait()
|
||||||
|
|
||||||
|
if removeIt {
|
||||||
|
err := cancelMibChangeNotify2(unicastAddressChangeHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
unicastAddressChangeHandle = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unicastAddressChanged(callerContext uintptr, row *MibUnicastIPAddressRow, notificationType MibNotificationType) uintptr {
|
||||||
|
rowCopy := *row
|
||||||
|
unicastAddressChangeMutex.Lock()
|
||||||
|
for cb := range unicastAddressChangeCallbacks {
|
||||||
|
cb.wait.Add(1)
|
||||||
|
go func(cb *UnicastAddressChangeCallback) {
|
||||||
|
cb.cb(notificationType, &rowCopy)
|
||||||
|
cb.wait.Done()
|
||||||
|
}(cb)
|
||||||
|
}
|
||||||
|
unicastAddressChangeMutex.Unlock()
|
||||||
|
return 0
|
||||||
|
}
|
193
listener/tun/dev/winipcfg/winipcfg.go
Normal file
193
listener/tun/dev/winipcfg/winipcfg.go
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Common functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys freeMibTable(memory unsafe.Pointer) = iphlpapi.FreeMibTable
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interface-related functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys initializeIPInterfaceEntry(row *MibIPInterfaceRow) = iphlpapi.InitializeIpInterfaceEntry
|
||||||
|
//sys getIPInterfaceTable(family AddressFamily, table **mibIPInterfaceTable) (ret error) = iphlpapi.GetIpInterfaceTable
|
||||||
|
//sys getIPInterfaceEntry(row *MibIPInterfaceRow) (ret error) = iphlpapi.GetIpInterfaceEntry
|
||||||
|
//sys setIPInterfaceEntry(row *MibIPInterfaceRow) (ret error) = iphlpapi.SetIpInterfaceEntry
|
||||||
|
//sys getIfEntry2(row *MibIfRow2) (ret error) = iphlpapi.GetIfEntry2
|
||||||
|
//sys getIfTable2Ex(level MibIfEntryLevel, table **mibIfTable2) (ret error) = iphlpapi.GetIfTable2Ex
|
||||||
|
//sys convertInterfaceLUIDToGUID(interfaceLUID *LUID, interfaceGUID *windows.GUID) (ret error) = iphlpapi.ConvertInterfaceLuidToGuid
|
||||||
|
//sys convertInterfaceGUIDToLUID(interfaceGUID *windows.GUID, interfaceLUID *LUID) (ret error) = iphlpapi.ConvertInterfaceGuidToLuid
|
||||||
|
//sys convertInterfaceIndexToLUID(interfaceIndex uint32, interfaceLUID *LUID) (ret error) = iphlpapi.ConvertInterfaceIndexToLuid
|
||||||
|
|
||||||
|
// GetAdaptersAddresses function retrieves the addresses associated with the adapters on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
|
||||||
|
func GetAdaptersAddresses(family AddressFamily, flags GAAFlags) ([]*IPAdapterAddresses, error) {
|
||||||
|
var b []byte
|
||||||
|
size := uint32(15000)
|
||||||
|
|
||||||
|
for {
|
||||||
|
b = make([]byte, size)
|
||||||
|
err := windows.GetAdaptersAddresses(uint32(family), uint32(flags), 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &size)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != windows.ERROR_BUFFER_OVERFLOW || size <= uint32(len(b)) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*IPAdapterAddresses, 0, uintptr(size)/unsafe.Sizeof(IPAdapterAddresses{}))
|
||||||
|
for wtiaa := (*IPAdapterAddresses)(unsafe.Pointer(&b[0])); wtiaa != nil; wtiaa = wtiaa.Next {
|
||||||
|
result = append(result, wtiaa)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPInterfaceTable function retrieves the IP interface entries on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipinterfacetable
|
||||||
|
func GetIPInterfaceTable(family AddressFamily) ([]MibIPInterfaceRow, error) {
|
||||||
|
var tab *mibIPInterfaceTable
|
||||||
|
err := getIPInterfaceTable(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := append(make([]MibIPInterfaceRow, 0, tab.numEntries), tab.get()...)
|
||||||
|
tab.free()
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIfTable2Ex function retrieves the MIB-II interface table.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getiftable2ex
|
||||||
|
func GetIfTable2Ex(level MibIfEntryLevel) ([]MibIfRow2, error) {
|
||||||
|
var tab *mibIfTable2
|
||||||
|
err := getIfTable2Ex(level, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := append(make([]MibIfRow2, 0, tab.numEntries), tab.get()...)
|
||||||
|
tab.free()
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unicast IP address-related functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys getUnicastIPAddressTable(family AddressFamily, table **mibUnicastIPAddressTable) (ret error) = iphlpapi.GetUnicastIpAddressTable
|
||||||
|
//sys initializeUnicastIPAddressEntry(row *MibUnicastIPAddressRow) = iphlpapi.InitializeUnicastIpAddressEntry
|
||||||
|
//sys getUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) = iphlpapi.GetUnicastIpAddressEntry
|
||||||
|
//sys setUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) = iphlpapi.SetUnicastIpAddressEntry
|
||||||
|
//sys createUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) = iphlpapi.CreateUnicastIpAddressEntry
|
||||||
|
//sys deleteUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) = iphlpapi.DeleteUnicastIpAddressEntry
|
||||||
|
|
||||||
|
// GetUnicastIPAddressTable function retrieves the unicast IP address table on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddresstable
|
||||||
|
func GetUnicastIPAddressTable(family AddressFamily) ([]MibUnicastIPAddressRow, error) {
|
||||||
|
var tab *mibUnicastIPAddressTable
|
||||||
|
err := getUnicastIPAddressTable(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := append(make([]MibUnicastIPAddressRow, 0, tab.numEntries), tab.get()...)
|
||||||
|
tab.free()
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Anycast IP address-related functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys getAnycastIPAddressTable(family AddressFamily, table **mibAnycastIPAddressTable) (ret error) = iphlpapi.GetAnycastIpAddressTable
|
||||||
|
//sys getAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) = iphlpapi.GetAnycastIpAddressEntry
|
||||||
|
//sys createAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) = iphlpapi.CreateAnycastIpAddressEntry
|
||||||
|
//sys deleteAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) = iphlpapi.DeleteAnycastIpAddressEntry
|
||||||
|
|
||||||
|
// GetAnycastIPAddressTable function retrieves the anycast IP address table on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getanycastipaddresstable
|
||||||
|
func GetAnycastIPAddressTable(family AddressFamily) ([]MibAnycastIPAddressRow, error) {
|
||||||
|
var tab *mibAnycastIPAddressTable
|
||||||
|
err := getAnycastIPAddressTable(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := append(make([]MibAnycastIPAddressRow, 0, tab.numEntries), tab.get()...)
|
||||||
|
tab.free()
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Routing-related functions
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys getIPForwardTable2(family AddressFamily, table **mibIPforwardTable2) (ret error) = iphlpapi.GetIpForwardTable2
|
||||||
|
//sys initializeIPForwardEntry(route *MibIPforwardRow2) = iphlpapi.InitializeIpForwardEntry
|
||||||
|
//sys getIPForwardEntry2(route *MibIPforwardRow2) (ret error) = iphlpapi.GetIpForwardEntry2
|
||||||
|
//sys setIPForwardEntry2(route *MibIPforwardRow2) (ret error) = iphlpapi.SetIpForwardEntry2
|
||||||
|
//sys createIPForwardEntry2(route *MibIPforwardRow2) (ret error) = iphlpapi.CreateIpForwardEntry2
|
||||||
|
//sys deleteIPForwardEntry2(route *MibIPforwardRow2) (ret error) = iphlpapi.DeleteIpForwardEntry2
|
||||||
|
|
||||||
|
// GetIPForwardTable2 function retrieves the IP route entries on the local computer.
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardtable2
|
||||||
|
func GetIPForwardTable2(family AddressFamily) ([]MibIPforwardRow2, error) {
|
||||||
|
var tab *mibIPforwardTable2
|
||||||
|
err := getIPForwardTable2(family, &tab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := append(make([]MibIPforwardRow2, 0, tab.numEntries), tab.get()...)
|
||||||
|
tab.free()
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Notifications-related functions
|
||||||
|
//
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-notifyipinterfacechange
|
||||||
|
//sys notifyIPInterfaceChange(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) = iphlpapi.NotifyIpInterfaceChange
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-notifyunicastipaddresschange
|
||||||
|
//sys notifyUnicastIPAddressChange(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) = iphlpapi.NotifyUnicastIpAddressChange
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-notifyroutechange2
|
||||||
|
//sys notifyRouteChange2(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) = iphlpapi.NotifyRouteChange2
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-cancelmibchangenotify2
|
||||||
|
//sys cancelMibChangeNotify2(notificationHandle windows.Handle) (ret error) = iphlpapi.CancelMibChangeNotify2
|
||||||
|
|
||||||
|
//
|
||||||
|
// Undocumented DNS API
|
||||||
|
//
|
||||||
|
|
||||||
|
//sys setInterfaceDnsSettingsByPtr(guid *windows.GUID, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?
|
||||||
|
//sys setInterfaceDnsSettingsByQwords(guid1 uintptr, guid2 uintptr, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?
|
||||||
|
//sys setInterfaceDnsSettingsByDwords(guid1 uintptr, guid2 uintptr, guid3 uintptr, guid4 uintptr, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?
|
||||||
|
|
||||||
|
// The GUID is passed by value, not by reference, which means different
|
||||||
|
// things on different calling conventions. On amd64, this means it's
|
||||||
|
// passed by reference anyway, while on arm, arm64, and 386, it's split
|
||||||
|
// into words.
|
||||||
|
func setInterfaceDnsSettings(guid windows.GUID, settings *dnsInterfaceSettings) error {
|
||||||
|
words := (*[4]uintptr)(unsafe.Pointer(&guid))
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "amd64":
|
||||||
|
return setInterfaceDnsSettingsByPtr(&guid, settings)
|
||||||
|
case "arm64":
|
||||||
|
return setInterfaceDnsSettingsByQwords(words[0], words[1], settings)
|
||||||
|
case "arm", "386":
|
||||||
|
return setInterfaceDnsSettingsByDwords(words[0], words[1], words[2], words[3], settings)
|
||||||
|
default:
|
||||||
|
panic("unknown calling convention")
|
||||||
|
}
|
||||||
|
}
|
350
listener/tun/dev/winipcfg/zwinipcfg_windows.go
Normal file
350
listener/tun/dev/winipcfg/zwinipcfg_windows.go
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package winipcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||||
|
|
||||||
|
procCancelMibChangeNotify2 = modiphlpapi.NewProc("CancelMibChangeNotify2")
|
||||||
|
procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
|
||||||
|
procConvertInterfaceIndexToLuid = modiphlpapi.NewProc("ConvertInterfaceIndexToLuid")
|
||||||
|
procConvertInterfaceLuidToGuid = modiphlpapi.NewProc("ConvertInterfaceLuidToGuid")
|
||||||
|
procCreateAnycastIpAddressEntry = modiphlpapi.NewProc("CreateAnycastIpAddressEntry")
|
||||||
|
procCreateIpForwardEntry2 = modiphlpapi.NewProc("CreateIpForwardEntry2")
|
||||||
|
procCreateUnicastIpAddressEntry = modiphlpapi.NewProc("CreateUnicastIpAddressEntry")
|
||||||
|
procDeleteAnycastIpAddressEntry = modiphlpapi.NewProc("DeleteAnycastIpAddressEntry")
|
||||||
|
procDeleteIpForwardEntry2 = modiphlpapi.NewProc("DeleteIpForwardEntry2")
|
||||||
|
procDeleteUnicastIpAddressEntry = modiphlpapi.NewProc("DeleteUnicastIpAddressEntry")
|
||||||
|
procFreeMibTable = modiphlpapi.NewProc("FreeMibTable")
|
||||||
|
procGetAnycastIpAddressEntry = modiphlpapi.NewProc("GetAnycastIpAddressEntry")
|
||||||
|
procGetAnycastIpAddressTable = modiphlpapi.NewProc("GetAnycastIpAddressTable")
|
||||||
|
procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2")
|
||||||
|
procGetIfTable2Ex = modiphlpapi.NewProc("GetIfTable2Ex")
|
||||||
|
procGetIpForwardEntry2 = modiphlpapi.NewProc("GetIpForwardEntry2")
|
||||||
|
procGetIpForwardTable2 = modiphlpapi.NewProc("GetIpForwardTable2")
|
||||||
|
procGetIpInterfaceEntry = modiphlpapi.NewProc("GetIpInterfaceEntry")
|
||||||
|
procGetIpInterfaceTable = modiphlpapi.NewProc("GetIpInterfaceTable")
|
||||||
|
procGetUnicastIpAddressEntry = modiphlpapi.NewProc("GetUnicastIpAddressEntry")
|
||||||
|
procGetUnicastIpAddressTable = modiphlpapi.NewProc("GetUnicastIpAddressTable")
|
||||||
|
procInitializeIpForwardEntry = modiphlpapi.NewProc("InitializeIpForwardEntry")
|
||||||
|
procInitializeIpInterfaceEntry = modiphlpapi.NewProc("InitializeIpInterfaceEntry")
|
||||||
|
procInitializeUnicastIpAddressEntry = modiphlpapi.NewProc("InitializeUnicastIpAddressEntry")
|
||||||
|
procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange")
|
||||||
|
procNotifyRouteChange2 = modiphlpapi.NewProc("NotifyRouteChange2")
|
||||||
|
procNotifyUnicastIpAddressChange = modiphlpapi.NewProc("NotifyUnicastIpAddressChange")
|
||||||
|
procSetInterfaceDnsSettings = modiphlpapi.NewProc("SetInterfaceDnsSettings")
|
||||||
|
procSetIpForwardEntry2 = modiphlpapi.NewProc("SetIpForwardEntry2")
|
||||||
|
procSetIpInterfaceEntry = modiphlpapi.NewProc("SetIpInterfaceEntry")
|
||||||
|
procSetUnicastIpAddressEntry = modiphlpapi.NewProc("SetUnicastIpAddressEntry")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cancelMibChangeNotify2(notificationHandle windows.Handle) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCancelMibChangeNotify2.Addr(), 1, uintptr(notificationHandle), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInterfaceGUIDToLUID(interfaceGUID *windows.GUID, interfaceLUID *LUID) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procConvertInterfaceGuidToLuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceGUID)), uintptr(unsafe.Pointer(interfaceLUID)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInterfaceIndexToLUID(interfaceIndex uint32, interfaceLUID *LUID) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procConvertInterfaceIndexToLuid.Addr(), 2, uintptr(interfaceIndex), uintptr(unsafe.Pointer(interfaceLUID)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInterfaceLUIDToGUID(interfaceLUID *LUID, interfaceGUID *windows.GUID) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procConvertInterfaceLuidToGuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceLUID)), uintptr(unsafe.Pointer(interfaceGUID)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCreateAnycastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIPForwardEntry2(route *MibIPforwardRow2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCreateIpForwardEntry2.Addr(), 1, uintptr(unsafe.Pointer(route)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCreateUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procDeleteAnycastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteIPForwardEntry2(route *MibIPforwardRow2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procDeleteIpForwardEntry2.Addr(), 1, uintptr(unsafe.Pointer(route)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procDeleteUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeMibTable(memory unsafe.Pointer) {
|
||||||
|
syscall.Syscall(procFreeMibTable.Addr(), 1, uintptr(memory), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAnycastIPAddressEntry(row *MibAnycastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetAnycastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAnycastIPAddressTable(family AddressFamily, table **mibAnycastIPAddressTable) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetAnycastIpAddressTable.Addr(), 2, uintptr(family), uintptr(unsafe.Pointer(table)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIfEntry2(row *MibIfRow2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIfEntry2.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIfTable2Ex(level MibIfEntryLevel, table **mibIfTable2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIfTable2Ex.Addr(), 2, uintptr(level), uintptr(unsafe.Pointer(table)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPForwardEntry2(route *MibIPforwardRow2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIpForwardEntry2.Addr(), 1, uintptr(unsafe.Pointer(route)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPForwardTable2(family AddressFamily, table **mibIPforwardTable2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIpForwardTable2.Addr(), 2, uintptr(family), uintptr(unsafe.Pointer(table)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPInterfaceEntry(row *MibIPInterfaceRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIpInterfaceEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIPInterfaceTable(family AddressFamily, table **mibIPInterfaceTable) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetIpInterfaceTable.Addr(), 2, uintptr(family), uintptr(unsafe.Pointer(table)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUnicastIPAddressTable(family AddressFamily, table **mibUnicastIPAddressTable) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetUnicastIpAddressTable.Addr(), 2, uintptr(family), uintptr(unsafe.Pointer(table)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeIPForwardEntry(route *MibIPforwardRow2) {
|
||||||
|
syscall.Syscall(procInitializeIpForwardEntry.Addr(), 1, uintptr(unsafe.Pointer(route)), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeIPInterfaceEntry(row *MibIPInterfaceRow) {
|
||||||
|
syscall.Syscall(procInitializeIpInterfaceEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeUnicastIPAddressEntry(row *MibUnicastIPAddressRow) {
|
||||||
|
syscall.Syscall(procInitializeUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyIPInterfaceChange(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if initialNotification {
|
||||||
|
_p0 = 1
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procNotifyIpInterfaceChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyRouteChange2(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if initialNotification {
|
||||||
|
_p0 = 1
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procNotifyRouteChange2.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyUnicastIPAddressChange(family AddressFamily, callback uintptr, callerContext uintptr, initialNotification bool, notificationHandle *windows.Handle) (ret error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if initialNotification {
|
||||||
|
_p0 = 1
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procNotifyUnicastIpAddressChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setInterfaceDnsSettingsByDwords(guid1 uintptr, guid2 uintptr, guid3 uintptr, guid4 uintptr, settings *dnsInterfaceSettings) (ret error) {
|
||||||
|
ret = procSetInterfaceDnsSettings.Find()
|
||||||
|
if ret != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procSetInterfaceDnsSettings.Addr(), 5, uintptr(guid1), uintptr(guid2), uintptr(guid3), uintptr(guid4), uintptr(unsafe.Pointer(settings)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setInterfaceDnsSettingsByPtr(guid *windows.GUID, settings *dnsInterfaceSettings) (ret error) {
|
||||||
|
ret = procSetInterfaceDnsSettings.Find()
|
||||||
|
if ret != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procSetInterfaceDnsSettings.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(settings)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setInterfaceDnsSettingsByQwords(guid1 uintptr, guid2 uintptr, settings *dnsInterfaceSettings) (ret error) {
|
||||||
|
ret = procSetInterfaceDnsSettings.Find()
|
||||||
|
if ret != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procSetInterfaceDnsSettings.Addr(), 3, uintptr(guid1), uintptr(guid2), uintptr(unsafe.Pointer(settings)))
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIPForwardEntry2(route *MibIPforwardRow2) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procSetIpForwardEntry2.Addr(), 1, uintptr(unsafe.Pointer(route)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIPInterfaceEntry(row *MibIPInterfaceRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procSetIpInterfaceEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUnicastIPAddressEntry(row *MibUnicastIPAddressRow) (ret error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procSetUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
ret = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
49
listener/tun/dev/wintun/dll_fromfile_windows.go
Normal file
49
listener/tun/dev/wintun/dll_fromfile_windows.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// +build !load_wintun_from_rsrc
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lazyDLL struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
module windows.Handle
|
||||||
|
onLoad func(d *lazyDLL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) Load() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.module != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
|
||||||
|
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||||
|
)
|
||||||
|
module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load library: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||||
|
if d.onLoad != nil {
|
||||||
|
d.onLoad(d)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||||
|
return windows.GetProcAddress(p.dll.module, p.Name)
|
||||||
|
}
|
56
listener/tun/dev/wintun/dll_fromrsrc_windows.go
Normal file
56
listener/tun/dev/wintun/dll_fromrsrc_windows.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// +build load_wintun_from_rsrc
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev/wintun/memmod"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lazyDLL struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
module *memmod.Module
|
||||||
|
onLoad func(d *lazyDLL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) Load() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.module != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const ourModule windows.Handle = 0
|
||||||
|
resInfo, err := windows.FindResource(ourModule, d.Name, windows.RT_RCDATA)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %w", d.Name, err)
|
||||||
|
}
|
||||||
|
data, err := windows.LoadResourceData(ourModule, resInfo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load resource: %w", err)
|
||||||
|
}
|
||||||
|
module, err := memmod.LoadLibrary(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load library: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||||
|
if d.onLoad != nil {
|
||||||
|
d.onLoad(d)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||||
|
return p.dll.module.ProcAddressByName(p.Name)
|
||||||
|
}
|
54
listener/tun/dev/wintun/dll_windows.go
Normal file
54
listener/tun/dev/wintun/dll_windows.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
||||||
|
return &lazyDLL{Name: name, onLoad: onLoad}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) NewProc(name string) *lazyProc {
|
||||||
|
return &lazyProc{dll: d, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lazyProc struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
dll *lazyDLL
|
||||||
|
addr uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Find() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.addr != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.dll.Load()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err)
|
||||||
|
}
|
||||||
|
addr, err := p.nameToAddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting %v address: %w", p.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Addr() uintptr {
|
||||||
|
err := p.Find()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return p.addr
|
||||||
|
}
|
620
listener/tun/dev/wintun/memmod/memmod_windows.go
Normal file
620
listener/tun/dev/wintun/memmod/memmod_windows.go
Normal file
|
@ -0,0 +1,620 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type addressList struct {
|
||||||
|
next *addressList
|
||||||
|
address uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (head *addressList) free() {
|
||||||
|
for node := head; node != nil; node = node.next {
|
||||||
|
windows.VirtualFree(node.address, 0, windows.MEM_RELEASE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Module struct {
|
||||||
|
headers *IMAGE_NT_HEADERS
|
||||||
|
codeBase uintptr
|
||||||
|
modules []windows.Handle
|
||||||
|
initialized bool
|
||||||
|
isDLL bool
|
||||||
|
isRelocated bool
|
||||||
|
nameExports map[string]uint16
|
||||||
|
entry uintptr
|
||||||
|
blockedMemory *addressList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY {
|
||||||
|
return &module.headers.OptionalHeader.DataDirectory[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) copySections(address uintptr, size uintptr, old_headers *IMAGE_NT_HEADERS) error {
|
||||||
|
sections := module.headers.Sections()
|
||||||
|
for i := range sections {
|
||||||
|
if sections[i].SizeOfRawData == 0 {
|
||||||
|
// Section doesn't contain data in the dll itself, but may define uninitialized data.
|
||||||
|
sectionSize := old_headers.OptionalHeader.SectionAlignment
|
||||||
|
if sectionSize == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
uintptr(sectionSize),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating section: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||||
|
dest = module.codeBase + uintptr(sections[i].VirtualAddress)
|
||||||
|
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||||
|
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||||
|
var dst []byte
|
||||||
|
unsafeSlice(unsafe.Pointer(&dst), a2p(dest), int(sectionSize))
|
||||||
|
for j := range dst {
|
||||||
|
dst[j] = 0
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) {
|
||||||
|
return errors.New("Incomplete section")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit memory block and copy data from dll.
|
||||||
|
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
uintptr(sections[i].SizeOfRawData),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||||
|
memcpy(
|
||||||
|
module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
address+uintptr(sections[i].PointerToRawData),
|
||||||
|
uintptr(sections[i].SizeOfRawData))
|
||||||
|
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||||
|
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr {
|
||||||
|
size := section.SizeOfRawData
|
||||||
|
if size != 0 {
|
||||||
|
return uintptr(size)
|
||||||
|
}
|
||||||
|
if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 {
|
||||||
|
return uintptr(module.headers.OptionalHeader.SizeOfInitializedData)
|
||||||
|
}
|
||||||
|
if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 {
|
||||||
|
return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type sectionFinalizeData struct {
|
||||||
|
address uintptr
|
||||||
|
alignedAddress uintptr
|
||||||
|
size uintptr
|
||||||
|
characteristics uint32
|
||||||
|
last bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error {
|
||||||
|
if sectionData.size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 {
|
||||||
|
// Section is not needed any more and can safely be freed.
|
||||||
|
if sectionData.address == sectionData.alignedAddress &&
|
||||||
|
(sectionData.last ||
|
||||||
|
(sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) {
|
||||||
|
// Only allowed to decommit whole pages.
|
||||||
|
windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine protection flags based on characteristics
|
||||||
|
var ProtectionFlags = [8]uint32{
|
||||||
|
windows.PAGE_NOACCESS, // not writeable, not readable, not executable
|
||||||
|
windows.PAGE_EXECUTE, // not writeable, not readable, executable
|
||||||
|
windows.PAGE_READONLY, // not writeable, readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_READ, // not writeable, readable, executable
|
||||||
|
windows.PAGE_WRITECOPY, // writeable, not readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable
|
||||||
|
windows.PAGE_READWRITE, // writeable, readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable
|
||||||
|
}
|
||||||
|
protect := ProtectionFlags[sectionData.characteristics>>29]
|
||||||
|
if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 {
|
||||||
|
protect |= windows.PAGE_NOCACHE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change memory access flags.
|
||||||
|
var oldProtect uint32
|
||||||
|
err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error protecting memory page: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) finalizeSections() error {
|
||||||
|
sections := module.headers.Sections()
|
||||||
|
imageOffset := module.headers.OptionalHeader.imageOffset()
|
||||||
|
sectionData := sectionFinalizeData{}
|
||||||
|
sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset
|
||||||
|
sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||||
|
sectionData.size = module.realSectionSize(§ions[0])
|
||||||
|
sectionData.characteristics = sections[0].Characteristics
|
||||||
|
|
||||||
|
// Loop through all sections and change access flags.
|
||||||
|
for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ {
|
||||||
|
sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset
|
||||||
|
alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||||
|
sectionSize := module.realSectionSize(§ions[i])
|
||||||
|
// Combine access flags of all sections that share a page.
|
||||||
|
// TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
|
||||||
|
if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress {
|
||||||
|
// Section shares page with previous.
|
||||||
|
if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 {
|
||||||
|
sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE
|
||||||
|
} else {
|
||||||
|
sectionData.characteristics |= sections[i].Characteristics
|
||||||
|
}
|
||||||
|
sectionData.size = sectionAddress + sectionSize - sectionData.address
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := module.finalizeSection(§ionData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error finalizing section: %w", err)
|
||||||
|
}
|
||||||
|
sectionData.address = sectionAddress
|
||||||
|
sectionData.alignedAddress = alignedAddress
|
||||||
|
sectionData.size = sectionSize
|
||||||
|
sectionData.characteristics = sections[i].Characteristics
|
||||||
|
}
|
||||||
|
sectionData.last = true
|
||||||
|
err := module.finalizeSection(§ionData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error finalizing section: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) executeTLS() {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS)
|
||||||
|
if directory.VirtualAddress == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
callback := tls.AddressOfCallbacks
|
||||||
|
if callback != 0 {
|
||||||
|
for {
|
||||||
|
f := *(*uintptr)(a2p(callback))
|
||||||
|
if f == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
syscall.Syscall(f, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), uintptr(0))
|
||||||
|
callback += unsafe.Sizeof(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return delta == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
for relocationHdr.VirtualAddress > 0 {
|
||||||
|
dest := module.codeBase + uintptr(relocationHdr.VirtualAddress)
|
||||||
|
|
||||||
|
var relInfos []uint16
|
||||||
|
unsafeSlice(
|
||||||
|
unsafe.Pointer(&relInfos),
|
||||||
|
a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr)),
|
||||||
|
int((uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(relInfos[0])))
|
||||||
|
for _, relInfo := range relInfos {
|
||||||
|
// The upper 4 bits define the type of relocation.
|
||||||
|
relType := relInfo >> 12
|
||||||
|
// The lower 12 bits define the offset.
|
||||||
|
relOffset := uintptr(relInfo & 0xfff)
|
||||||
|
|
||||||
|
switch relType {
|
||||||
|
case IMAGE_REL_BASED_ABSOLUTE:
|
||||||
|
// Skip relocation.
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_LOW:
|
||||||
|
*(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff)
|
||||||
|
break
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_HIGH:
|
||||||
|
*(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16)
|
||||||
|
break
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_HIGHLOW:
|
||||||
|
*(*uint32)(a2p(dest + relOffset)) += uint32(delta)
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_DIR64:
|
||||||
|
*(*uint64)(a2p(dest + relOffset)) += uint64(delta)
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_THUMB_MOV32:
|
||||||
|
inst := *(*uint32)(a2p(dest + relOffset))
|
||||||
|
imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||||
|
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||||
|
if (inst & 0x8000fbf0) != 0x0000f240 {
|
||||||
|
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst)
|
||||||
|
}
|
||||||
|
imm16 += uint32(delta) & 0xffff
|
||||||
|
hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16)
|
||||||
|
*(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) +
|
||||||
|
((imm16 >> 12) & 0x000f) +
|
||||||
|
((imm16 << 20) & 0x70000000) +
|
||||||
|
((imm16 << 16) & 0xff0000)
|
||||||
|
if hiDelta != 0 {
|
||||||
|
inst = *(*uint32)(a2p(dest + relOffset + 4))
|
||||||
|
imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||||
|
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||||
|
if (inst & 0x8000fbf0) != 0x0000f2c0 {
|
||||||
|
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst)
|
||||||
|
}
|
||||||
|
imm16 += hiDelta
|
||||||
|
if imm16 > 0xffff {
|
||||||
|
return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16)
|
||||||
|
}
|
||||||
|
*(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) +
|
||||||
|
((imm16 >> 1) & 0x0400) +
|
||||||
|
((imm16 >> 12) & 0x000f) +
|
||||||
|
((imm16 << 20) & 0x70000000) +
|
||||||
|
((imm16 << 16) & 0xff0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("Unsupported relocation: %v", relType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to next relocation block.
|
||||||
|
relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock)))
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) buildImportTable() error {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
module.modules = make([]windows.Handle, 0, 16)
|
||||||
|
importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
for importDesc.Name != 0 {
|
||||||
|
handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading module: %w", err)
|
||||||
|
}
|
||||||
|
var thunkRef, funcRef *uintptr
|
||||||
|
if importDesc.OriginalFirstThunk() != 0 {
|
||||||
|
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk())))
|
||||||
|
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
} else {
|
||||||
|
// No hint table.
|
||||||
|
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
}
|
||||||
|
for *thunkRef != 0 {
|
||||||
|
if IMAGE_SNAP_BY_ORDINAL(*thunkRef) {
|
||||||
|
*funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef))
|
||||||
|
} else {
|
||||||
|
thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef))
|
||||||
|
*funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0]))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
windows.FreeLibrary(handle)
|
||||||
|
return fmt.Errorf("Error getting function address: %w", err)
|
||||||
|
}
|
||||||
|
thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef)))
|
||||||
|
funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef)))
|
||||||
|
}
|
||||||
|
module.modules = append(module.modules, handle)
|
||||||
|
importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) buildNameExports() error {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 {
|
||||||
|
return errors.New("No functions exported")
|
||||||
|
}
|
||||||
|
if exports.NumberOfNames == 0 {
|
||||||
|
return errors.New("No functions exported by name")
|
||||||
|
}
|
||||||
|
var nameRefs []uint32
|
||||||
|
unsafeSlice(unsafe.Pointer(&nameRefs), a2p(module.codeBase+uintptr(exports.AddressOfNames)), int(exports.NumberOfNames))
|
||||||
|
var ordinals []uint16
|
||||||
|
unsafeSlice(unsafe.Pointer(&ordinals), a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals)), int(exports.NumberOfNames))
|
||||||
|
module.nameExports = make(map[string]uint16)
|
||||||
|
for i := range nameRefs {
|
||||||
|
nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i]))))
|
||||||
|
module.nameExports[nameArray] = ordinals[i]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadLibrary loads module image to memory.
|
||||||
|
func LoadLibrary(data []byte) (module *Module, err error) {
|
||||||
|
addr := uintptr(unsafe.Pointer(&data[0]))
|
||||||
|
size := uintptr(len(data))
|
||||||
|
if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) {
|
||||||
|
return nil, errors.New("Incomplete IMAGE_DOS_HEADER")
|
||||||
|
}
|
||||||
|
dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr))
|
||||||
|
if dosHeader.E_magic != IMAGE_DOS_SIGNATURE {
|
||||||
|
return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE)
|
||||||
|
}
|
||||||
|
if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) {
|
||||||
|
return nil, errors.New("Incomplete IMAGE_NT_HEADERS")
|
||||||
|
}
|
||||||
|
oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew)))
|
||||||
|
if oldHeader.Signature != IMAGE_NT_SIGNATURE {
|
||||||
|
return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE)
|
||||||
|
}
|
||||||
|
if oldHeader.FileHeader.Machine != imageFileProcess {
|
||||||
|
return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess)
|
||||||
|
}
|
||||||
|
if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 {
|
||||||
|
return nil, errors.New("Unaligned section")
|
||||||
|
}
|
||||||
|
lastSectionEnd := uintptr(0)
|
||||||
|
sections := oldHeader.Sections()
|
||||||
|
optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment
|
||||||
|
for i := range sections {
|
||||||
|
var endOfSection uintptr
|
||||||
|
if sections[i].SizeOfRawData == 0 {
|
||||||
|
// Section without data in the DLL
|
||||||
|
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize)
|
||||||
|
} else {
|
||||||
|
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData)
|
||||||
|
}
|
||||||
|
if endOfSection > lastSectionEnd {
|
||||||
|
lastSectionEnd = endOfSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment))
|
||||||
|
if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) {
|
||||||
|
return nil, errors.New("Section is not page-aligned")
|
||||||
|
}
|
||||||
|
|
||||||
|
module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
module.Free()
|
||||||
|
module = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Reserve memory for image of library.
|
||||||
|
// TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't.
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
// Try to allocate memory at arbitrary position.
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(0,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error allocating code: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = module.check4GBBoundaries(alignedImageSize)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reallocating code: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) {
|
||||||
|
err = errors.New("Incomplete headers")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Commit memory for headers.
|
||||||
|
headers, err := windows.VirtualAlloc(module.codeBase,
|
||||||
|
uintptr(oldHeader.OptionalHeader.SizeOfHeaders),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error allocating headers: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Copy PE header to code.
|
||||||
|
memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders))
|
||||||
|
module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew)))
|
||||||
|
|
||||||
|
// Update position.
|
||||||
|
module.headers.OptionalHeader.ImageBase = module.codeBase
|
||||||
|
|
||||||
|
// Copy sections from DLL file block to new memory location.
|
||||||
|
err = module.copySections(addr, size, oldHeader)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error copying sections: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust base address of imported data.
|
||||||
|
locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase
|
||||||
|
if locationDelta != 0 {
|
||||||
|
module.isRelocated, err = module.performBaseRelocation(locationDelta)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error relocating module: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
module.isRelocated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load required dlls and adjust function table of imports.
|
||||||
|
err = module.buildImportTable()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error building import table: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark memory pages depending on section headers and release sections that are marked as "discardable".
|
||||||
|
err = module.finalizeSections()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finalizing sections: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS callbacks are executed BEFORE the main loading.
|
||||||
|
module.executeTLS()
|
||||||
|
|
||||||
|
// Get entry point of loaded module.
|
||||||
|
if module.headers.OptionalHeader.AddressOfEntryPoint != 0 {
|
||||||
|
module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint)
|
||||||
|
if module.isDLL {
|
||||||
|
// Notify library about attaching to process.
|
||||||
|
r0, _, _ := syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), 0)
|
||||||
|
successful := r0 != 0
|
||||||
|
if !successful {
|
||||||
|
err = windows.ERROR_DLL_INIT_FAILED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
module.initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.buildNameExports()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free releases module resources and unloads it.
|
||||||
|
func (module *Module) Free() {
|
||||||
|
if module.initialized {
|
||||||
|
// Notify library about detaching from process.
|
||||||
|
syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_DETACH), 0)
|
||||||
|
module.initialized = false
|
||||||
|
}
|
||||||
|
if module.modules != nil {
|
||||||
|
// Free previously opened libraries.
|
||||||
|
for _, handle := range module.modules {
|
||||||
|
windows.FreeLibrary(handle)
|
||||||
|
}
|
||||||
|
module.modules = nil
|
||||||
|
}
|
||||||
|
if module.codeBase != 0 {
|
||||||
|
windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE)
|
||||||
|
module.codeBase = 0
|
||||||
|
}
|
||||||
|
if module.blockedMemory != nil {
|
||||||
|
module.blockedMemory.free()
|
||||||
|
module.blockedMemory = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcAddressByName returns function address by exported name.
|
||||||
|
func (module *Module) ProcAddressByName(name string) (uintptr, error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return 0, errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if module.nameExports == nil {
|
||||||
|
return 0, errors.New("No functions exported by name")
|
||||||
|
}
|
||||||
|
if idx, ok := module.nameExports[name]; ok {
|
||||||
|
if uint32(idx) > exports.NumberOfFunctions {
|
||||||
|
return 0, errors.New("Ordinal number too high")
|
||||||
|
}
|
||||||
|
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||||
|
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("Function not found by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcAddressByOrdinal returns function address by exported ordinal.
|
||||||
|
func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return 0, errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if uint32(ordinal) < exports.Base {
|
||||||
|
return 0, errors.New("Ordinal number too low")
|
||||||
|
}
|
||||||
|
idx := ordinal - uint16(exports.Base)
|
||||||
|
if uint32(idx) > exports.NumberOfFunctions {
|
||||||
|
return 0, errors.New("Ordinal number too high")
|
||||||
|
}
|
||||||
|
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||||
|
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func alignDown(value, alignment uintptr) uintptr {
|
||||||
|
return value & ^(alignment - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func alignUp(value, alignment uintptr) uintptr {
|
||||||
|
return (value + alignment - 1) & ^(alignment - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func a2p(addr uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func memcpy(dst, src, size uintptr) {
|
||||||
|
var d, s []byte
|
||||||
|
unsafeSlice(unsafe.Pointer(&d), a2p(dst), int(size))
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), a2p(src), int(size))
|
||||||
|
copy(d, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeSlice updates the slice slicePtr to be a slice
|
||||||
|
// referencing the provided data with its length & capacity set to
|
||||||
|
// lenCap.
|
||||||
|
//
|
||||||
|
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||||
|
// update callers to use unsafe.Slice instead of this.
|
||||||
|
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||||
|
type sliceHeader struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
h := (*sliceHeader)(slicePtr)
|
||||||
|
h.Data = data
|
||||||
|
h.Len = lenCap
|
||||||
|
h.Cap = lenCap
|
||||||
|
}
|
16
listener/tun/dev/wintun/memmod/memmod_windows_32.go
Normal file
16
listener/tun/dev/wintun/memmod/memmod_windows_32.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build windows,386 windows,arm
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||||
|
return
|
||||||
|
}
|
8
listener/tun/dev/wintun/memmod/memmod_windows_386.go
Normal file
8
listener/tun/dev/wintun/memmod/memmod_windows_386.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_I386
|
36
listener/tun/dev/wintun/memmod/memmod_windows_64.go
Normal file
36
listener/tun/dev/wintun/memmod/memmod_windows_64.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// +build windows,amd64 windows,arm64
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||||
|
return uintptr(opthdr.ImageBase & 0xffffffff00000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||||
|
for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) {
|
||||||
|
node := &addressList{
|
||||||
|
next: module.blockedMemory,
|
||||||
|
address: module.codeBase,
|
||||||
|
}
|
||||||
|
module.blockedMemory = node
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(0,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
8
listener/tun/dev/wintun/memmod/memmod_windows_amd64.go
Normal file
8
listener/tun/dev/wintun/memmod/memmod_windows_amd64.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_AMD64
|
8
listener/tun/dev/wintun/memmod/memmod_windows_arm.go
Normal file
8
listener/tun/dev/wintun/memmod/memmod_windows_arm.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT
|
8
listener/tun/dev/wintun/memmod/memmod_windows_arm64.go
Normal file
8
listener/tun/dev/wintun/memmod/memmod_windows_arm64.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_ARM64
|
339
listener/tun/dev/wintun/memmod/syscall_windows.go
Normal file
339
listener/tun/dev/wintun/memmod/syscall_windows.go
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_DOS_SIGNATURE = 0x5A4D // MZ
|
||||||
|
IMAGE_OS2_SIGNATURE = 0x454E // NE
|
||||||
|
IMAGE_OS2_SIGNATURE_LE = 0x454C // LE
|
||||||
|
IMAGE_VXD_SIGNATURE = 0x454C // LE
|
||||||
|
IMAGE_NT_SIGNATURE = 0x00004550 // PE00
|
||||||
|
)
|
||||||
|
|
||||||
|
// DOS .EXE header
|
||||||
|
type IMAGE_DOS_HEADER struct {
|
||||||
|
E_magic uint16 // Magic number
|
||||||
|
E_cblp uint16 // Bytes on last page of file
|
||||||
|
E_cp uint16 // Pages in file
|
||||||
|
E_crlc uint16 // Relocations
|
||||||
|
E_cparhdr uint16 // Size of header in paragraphs
|
||||||
|
E_minalloc uint16 // Minimum extra paragraphs needed
|
||||||
|
E_maxalloc uint16 // Maximum extra paragraphs needed
|
||||||
|
E_ss uint16 // Initial (relative) SS value
|
||||||
|
E_sp uint16 // Initial SP value
|
||||||
|
E_csum uint16 // Checksum
|
||||||
|
E_ip uint16 // Initial IP value
|
||||||
|
E_cs uint16 // Initial (relative) CS value
|
||||||
|
E_lfarlc uint16 // File address of relocation table
|
||||||
|
E_ovno uint16 // Overlay number
|
||||||
|
E_res [4]uint16 // Reserved words
|
||||||
|
E_oemid uint16 // OEM identifier (for e_oeminfo)
|
||||||
|
E_oeminfo uint16 // OEM information; e_oemid specific
|
||||||
|
E_res2 [10]uint16 // Reserved words
|
||||||
|
E_lfanew int32 // File address of new exe header
|
||||||
|
}
|
||||||
|
|
||||||
|
// File header format
|
||||||
|
type IMAGE_FILE_HEADER struct {
|
||||||
|
Machine uint16
|
||||||
|
NumberOfSections uint16
|
||||||
|
TimeDateStamp uint32
|
||||||
|
PointerToSymbolTable uint32
|
||||||
|
NumberOfSymbols uint32
|
||||||
|
SizeOfOptionalHeader uint16
|
||||||
|
Characteristics uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_SIZEOF_FILE_HEADER = 20
|
||||||
|
|
||||||
|
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file.
|
||||||
|
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references).
|
||||||
|
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file.
|
||||||
|
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file.
|
||||||
|
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set
|
||||||
|
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses
|
||||||
|
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed.
|
||||||
|
IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine.
|
||||||
|
IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file
|
||||||
|
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file.
|
||||||
|
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file.
|
||||||
|
IMAGE_FILE_SYSTEM = 0x1000 // System File.
|
||||||
|
IMAGE_FILE_DLL = 0x2000 // File is a DLL.
|
||||||
|
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine
|
||||||
|
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed.
|
||||||
|
|
||||||
|
IMAGE_FILE_MACHINE_UNKNOWN = 0
|
||||||
|
IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
|
||||||
|
IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386.
|
||||||
|
IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian
|
||||||
|
IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian
|
||||||
|
IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian
|
||||||
|
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2
|
||||||
|
IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP
|
||||||
|
IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
|
||||||
|
IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5
|
||||||
|
IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_AM33 = 0x01d3
|
||||||
|
IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
|
||||||
|
IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64
|
||||||
|
IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64
|
||||||
|
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64
|
||||||
|
IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon
|
||||||
|
IMAGE_FILE_MACHINE_CEF = 0x0CEF
|
||||||
|
IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code
|
||||||
|
IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8)
|
||||||
|
IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian
|
||||||
|
IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_CEE = 0xC0EE
|
||||||
|
)
|
||||||
|
|
||||||
|
// Directory format
|
||||||
|
type IMAGE_DATA_DIRECTORY struct {
|
||||||
|
VirtualAddress uint32
|
||||||
|
Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
||||||
|
|
||||||
|
type IMAGE_NT_HEADERS struct {
|
||||||
|
Signature uint32
|
||||||
|
FileHeader IMAGE_FILE_HEADER
|
||||||
|
OptionalHeader IMAGE_OPTIONAL_HEADER
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER {
|
||||||
|
return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer(
|
||||||
|
(uintptr)(unsafe.Pointer(ntheader)) +
|
||||||
|
unsafe.Offsetof(ntheader.OptionalHeader) +
|
||||||
|
uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table
|
||||||
|
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage)
|
||||||
|
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data
|
||||||
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP
|
||||||
|
IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers
|
||||||
|
IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table
|
||||||
|
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors
|
||||||
|
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
const IMAGE_SIZEOF_SHORT_NAME = 8
|
||||||
|
|
||||||
|
// Section header format
|
||||||
|
type IMAGE_SECTION_HEADER struct {
|
||||||
|
Name [IMAGE_SIZEOF_SHORT_NAME]byte
|
||||||
|
physicalAddressOrVirtualSize uint32
|
||||||
|
VirtualAddress uint32
|
||||||
|
SizeOfRawData uint32
|
||||||
|
PointerToRawData uint32
|
||||||
|
PointerToRelocations uint32
|
||||||
|
PointerToLinenumbers uint32
|
||||||
|
NumberOfRelocations uint16
|
||||||
|
NumberOfLinenumbers uint16
|
||||||
|
Characteristics uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 {
|
||||||
|
return ishdr.physicalAddressOrVirtualSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) {
|
||||||
|
ishdr.physicalAddressOrVirtualSize = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 {
|
||||||
|
return ishdr.physicalAddressOrVirtualSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) {
|
||||||
|
ishdr.physicalAddressOrVirtualSize = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Section characteristics.
|
||||||
|
IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved.
|
||||||
|
|
||||||
|
IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code.
|
||||||
|
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data.
|
||||||
|
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data.
|
||||||
|
|
||||||
|
IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved.
|
||||||
|
IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information.
|
||||||
|
IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved.
|
||||||
|
IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image.
|
||||||
|
IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat.
|
||||||
|
IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete.
|
||||||
|
IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||||
|
IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP
|
||||||
|
IMAGE_SCN_MEM_FARDATA = 0x00008000
|
||||||
|
IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete.
|
||||||
|
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
|
||||||
|
IMAGE_SCN_MEM_16BIT = 0x00020000
|
||||||
|
IMAGE_SCN_MEM_LOCKED = 0x00040000
|
||||||
|
IMAGE_SCN_MEM_PRELOAD = 0x00080000
|
||||||
|
|
||||||
|
IMAGE_SCN_ALIGN_1BYTES = 0x00100000 //
|
||||||
|
IMAGE_SCN_ALIGN_2BYTES = 0x00200000 //
|
||||||
|
IMAGE_SCN_ALIGN_4BYTES = 0x00300000 //
|
||||||
|
IMAGE_SCN_ALIGN_8BYTES = 0x00400000 //
|
||||||
|
IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified.
|
||||||
|
IMAGE_SCN_ALIGN_32BYTES = 0x00600000 //
|
||||||
|
IMAGE_SCN_ALIGN_64BYTES = 0x00700000 //
|
||||||
|
IMAGE_SCN_ALIGN_128BYTES = 0x00800000 //
|
||||||
|
IMAGE_SCN_ALIGN_256BYTES = 0x00900000 //
|
||||||
|
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 //
|
||||||
|
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 //
|
||||||
|
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 //
|
||||||
|
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 //
|
||||||
|
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 //
|
||||||
|
IMAGE_SCN_ALIGN_MASK = 0x00F00000
|
||||||
|
|
||||||
|
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations.
|
||||||
|
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded.
|
||||||
|
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable.
|
||||||
|
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable.
|
||||||
|
IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable.
|
||||||
|
IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable.
|
||||||
|
IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable.
|
||||||
|
IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable.
|
||||||
|
|
||||||
|
// TLS Characteristic Flags
|
||||||
|
IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Based relocation format
|
||||||
|
type IMAGE_BASE_RELOCATION struct {
|
||||||
|
VirtualAddress uint32
|
||||||
|
SizeOfBlock uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_REL_BASED_ABSOLUTE = 0
|
||||||
|
IMAGE_REL_BASED_HIGH = 1
|
||||||
|
IMAGE_REL_BASED_LOW = 2
|
||||||
|
IMAGE_REL_BASED_HIGHLOW = 3
|
||||||
|
IMAGE_REL_BASED_HIGHADJ = 4
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5
|
||||||
|
IMAGE_REL_BASED_RESERVED = 6
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9
|
||||||
|
IMAGE_REL_BASED_DIR64 = 10
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_IA64_IMM64 = 9
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_MIPS_JMPADDR = 5
|
||||||
|
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_ARM_MOV32 = 5
|
||||||
|
IMAGE_REL_BASED_THUMB_MOV32 = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Export Format
|
||||||
|
type IMAGE_EXPORT_DIRECTORY struct {
|
||||||
|
Characteristics uint32
|
||||||
|
TimeDateStamp uint32
|
||||||
|
MajorVersion uint16
|
||||||
|
MinorVersion uint16
|
||||||
|
Name uint32
|
||||||
|
Base uint32
|
||||||
|
NumberOfFunctions uint32
|
||||||
|
NumberOfNames uint32
|
||||||
|
AddressOfFunctions uint32 // RVA from base of image
|
||||||
|
AddressOfNames uint32 // RVA from base of image
|
||||||
|
AddressOfNameOrdinals uint32 // RVA from base of image
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMAGE_IMPORT_BY_NAME struct {
|
||||||
|
Hint uint16
|
||||||
|
Name [1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func IMAGE_ORDINAL(ordinal uintptr) uintptr {
|
||||||
|
return ordinal & 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool {
|
||||||
|
return (ordinal & IMAGE_ORDINAL_FLAG) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread Local Storage
|
||||||
|
type IMAGE_TLS_DIRECTORY struct {
|
||||||
|
StartAddressOfRawData uintptr
|
||||||
|
EndAddressOfRawData uintptr
|
||||||
|
AddressOfIndex uintptr // PDWORD
|
||||||
|
AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *;
|
||||||
|
SizeOfZeroFill uint32
|
||||||
|
Characteristics uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMAGE_IMPORT_DESCRIPTOR struct {
|
||||||
|
characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor
|
||||||
|
// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
||||||
|
TimeDateStamp uint32 // 0 if not bound,
|
||||||
|
// -1 if bound, and real date\time stamp
|
||||||
|
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||||
|
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||||
|
ForwarderChain uint32 // -1 if no forwarders
|
||||||
|
Name uint32
|
||||||
|
FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 {
|
||||||
|
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 {
|
||||||
|
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DLL_PROCESS_ATTACH = 1
|
||||||
|
DLL_THREAD_ATTACH = 2
|
||||||
|
DLL_THREAD_DETACH = 3
|
||||||
|
DLL_PROCESS_DETACH = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type SYSTEM_INFO struct {
|
||||||
|
ProcessorArchitecture uint16
|
||||||
|
Reserved uint16
|
||||||
|
PageSize uint32
|
||||||
|
MinimumApplicationAddress uintptr
|
||||||
|
MaximumApplicationAddress uintptr
|
||||||
|
ActiveProcessorMask uintptr
|
||||||
|
NumberOfProcessors uint32
|
||||||
|
ProcessorType uint32
|
||||||
|
AllocationGranularity uint32
|
||||||
|
ProcessorLevel uint16
|
||||||
|
ProcessorRevision uint16
|
||||||
|
}
|
45
listener/tun/dev/wintun/memmod/syscall_windows_32.go
Normal file
45
listener/tun/dev/wintun/memmod/syscall_windows_32.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// +build windows,386 windows,arm
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
// Optional header format
|
||||||
|
type IMAGE_OPTIONAL_HEADER struct {
|
||||||
|
Magic uint16
|
||||||
|
MajorLinkerVersion uint8
|
||||||
|
MinorLinkerVersion uint8
|
||||||
|
SizeOfCode uint32
|
||||||
|
SizeOfInitializedData uint32
|
||||||
|
SizeOfUninitializedData uint32
|
||||||
|
AddressOfEntryPoint uint32
|
||||||
|
BaseOfCode uint32
|
||||||
|
BaseOfData uint32
|
||||||
|
ImageBase uintptr
|
||||||
|
SectionAlignment uint32
|
||||||
|
FileAlignment uint32
|
||||||
|
MajorOperatingSystemVersion uint16
|
||||||
|
MinorOperatingSystemVersion uint16
|
||||||
|
MajorImageVersion uint16
|
||||||
|
MinorImageVersion uint16
|
||||||
|
MajorSubsystemVersion uint16
|
||||||
|
MinorSubsystemVersion uint16
|
||||||
|
Win32VersionValue uint32
|
||||||
|
SizeOfImage uint32
|
||||||
|
SizeOfHeaders uint32
|
||||||
|
CheckSum uint32
|
||||||
|
Subsystem uint16
|
||||||
|
DllCharacteristics uint16
|
||||||
|
SizeOfStackReserve uintptr
|
||||||
|
SizeOfStackCommit uintptr
|
||||||
|
SizeOfHeapReserve uintptr
|
||||||
|
SizeOfHeapCommit uintptr
|
||||||
|
LoaderFlags uint32
|
||||||
|
NumberOfRvaAndSizes uint32
|
||||||
|
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_ORDINAL_FLAG uintptr = 0x80000000
|
44
listener/tun/dev/wintun/memmod/syscall_windows_64.go
Normal file
44
listener/tun/dev/wintun/memmod/syscall_windows_64.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// +build windows,amd64 windows,arm64
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
// Optional header format
|
||||||
|
type IMAGE_OPTIONAL_HEADER struct {
|
||||||
|
Magic uint16
|
||||||
|
MajorLinkerVersion uint8
|
||||||
|
MinorLinkerVersion uint8
|
||||||
|
SizeOfCode uint32
|
||||||
|
SizeOfInitializedData uint32
|
||||||
|
SizeOfUninitializedData uint32
|
||||||
|
AddressOfEntryPoint uint32
|
||||||
|
BaseOfCode uint32
|
||||||
|
ImageBase uintptr
|
||||||
|
SectionAlignment uint32
|
||||||
|
FileAlignment uint32
|
||||||
|
MajorOperatingSystemVersion uint16
|
||||||
|
MinorOperatingSystemVersion uint16
|
||||||
|
MajorImageVersion uint16
|
||||||
|
MinorImageVersion uint16
|
||||||
|
MajorSubsystemVersion uint16
|
||||||
|
MinorSubsystemVersion uint16
|
||||||
|
Win32VersionValue uint32
|
||||||
|
SizeOfImage uint32
|
||||||
|
SizeOfHeaders uint32
|
||||||
|
CheckSum uint32
|
||||||
|
Subsystem uint16
|
||||||
|
DllCharacteristics uint16
|
||||||
|
SizeOfStackReserve uintptr
|
||||||
|
SizeOfStackCommit uintptr
|
||||||
|
SizeOfHeapReserve uintptr
|
||||||
|
SizeOfHeapCommit uintptr
|
||||||
|
LoaderFlags uint32
|
||||||
|
NumberOfRvaAndSizes uint32
|
||||||
|
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000
|
103
listener/tun/dev/wintun/session_windows.go
Normal file
103
listener/tun/dev/wintun/session_windows.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
handle uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PacketSizeMax = 0xffff // Maximum packet size
|
||||||
|
RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB)
|
||||||
|
RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Packet with data
|
||||||
|
type Packet struct {
|
||||||
|
Next *Packet // Pointer to next packet in queue
|
||||||
|
Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE)
|
||||||
|
Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket")
|
||||||
|
procWintunEndSession = modwintun.NewProc("WintunEndSession")
|
||||||
|
procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent")
|
||||||
|
procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket")
|
||||||
|
procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket")
|
||||||
|
procWintunSendPacket = modwintun.NewProc("WintunSendPacket")
|
||||||
|
procWintunStartSession = modwintun.NewProc("WintunStartSession")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
} else {
|
||||||
|
session = Session{r0}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) End() {
|
||||||
|
syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0)
|
||||||
|
session.handle = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReadWaitEvent() (handle windows.Handle) {
|
||||||
|
r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0)
|
||||||
|
handle = windows.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReceivePacket() (packet []byte, err error) {
|
||||||
|
var packetSize uint32
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) ReleaseReceivePacket(packet []byte) {
|
||||||
|
syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session Session) SendPacket(packet []byte) {
|
||||||
|
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeSlice updates the slice slicePtr to be a slice
|
||||||
|
// referencing the provided data with its length & capacity set to
|
||||||
|
// lenCap.
|
||||||
|
//
|
||||||
|
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||||
|
// update callers to use unsafe.Slice instead of this.
|
||||||
|
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||||
|
type sliceHeader struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
h := (*sliceHeader)(slicePtr)
|
||||||
|
h.Data = data
|
||||||
|
h.Len = lenCap
|
||||||
|
h.Cap = lenCap
|
||||||
|
}
|
221
listener/tun/dev/wintun/wintun_windows.go
Normal file
221
listener/tun/dev/wintun/wintun_windows.go
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type loggerLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
logInfo loggerLevel = iota
|
||||||
|
logWarn
|
||||||
|
logErr
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PoolNameMax = 256
|
||||||
|
AdapterNameMax = 128
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pool [PoolNameMax]uint16
|
||||||
|
type Adapter struct {
|
||||||
|
handle uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
||||||
|
|
||||||
|
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
||||||
|
procWintunDeleteAdapter = modwintun.NewProc("WintunDeleteAdapter")
|
||||||
|
procWintunDeletePoolDriver = modwintun.NewProc("WintunDeletePoolDriver")
|
||||||
|
procWintunEnumAdapters = modwintun.NewProc("WintunEnumAdapters")
|
||||||
|
procWintunFreeAdapter = modwintun.NewProc("WintunFreeAdapter")
|
||||||
|
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
|
||||||
|
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
|
||||||
|
procWintunGetAdapterName = modwintun.NewProc("WintunGetAdapterName")
|
||||||
|
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
|
||||||
|
procWintunSetAdapterName = modwintun.NewProc("WintunSetAdapterName")
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupLogger(dll *lazyDLL) {
|
||||||
|
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, windows.NewCallback(func(level loggerLevel, msg *uint16) int {
|
||||||
|
var lv log.LogLevel
|
||||||
|
switch level {
|
||||||
|
case logInfo:
|
||||||
|
lv = log.INFO
|
||||||
|
case logWarn:
|
||||||
|
lv = log.WARNING
|
||||||
|
case logErr:
|
||||||
|
lv = log.ERROR
|
||||||
|
default:
|
||||||
|
lv = log.INFO
|
||||||
|
}
|
||||||
|
log.PrintLog(lv, "[Wintun] %s", windows.UTF16PtrToString(msg))
|
||||||
|
return 0
|
||||||
|
}), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakePool(poolName string) (pool *Pool, err error) {
|
||||||
|
poolName16, err := windows.UTF16FromString(poolName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(poolName16) > PoolNameMax {
|
||||||
|
err = errors.New("Pool name too long")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pool = &Pool{}
|
||||||
|
copy(pool[:], poolName16)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *Pool) String() string {
|
||||||
|
return windows.UTF16ToString(pool[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeAdapter(wintun *Adapter) {
|
||||||
|
syscall.Syscall(procWintunFreeAdapter.Addr(), 1, uintptr(wintun.handle), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenAdapter finds a Wintun adapter by its name. This function returns the adapter if found, or
|
||||||
|
// windows.ERROR_FILE_NOT_FOUND otherwise. If the adapter is found but not a Wintun-class or a
|
||||||
|
// member of the pool, this function returns windows.ERROR_ALREADY_EXISTS. The adapter must be
|
||||||
|
// released after use.
|
||||||
|
func (pool *Pool) OpenAdapter(ifname string) (wintun *Adapter, err error) {
|
||||||
|
ifname16, err := windows.UTF16PtrFromString(ifname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), 0)
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wintun = &Adapter{r0}
|
||||||
|
runtime.SetFinalizer(wintun, freeAdapter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAdapter creates a Wintun adapter. ifname is the requested name of the adapter, while
|
||||||
|
// requestedGUID is the GUID of the created network adapter, which then influences NLA generation
|
||||||
|
// deterministically. If it is set to nil, the GUID is chosen by the system at random, and hence a
|
||||||
|
// new NLA entry is created for each new adapter. It is called "requested" GUID because the API it
|
||||||
|
// uses is completely undocumented, and so there could be minor interesting complications with its
|
||||||
|
// usage. This function returns the network adapter ID and a flag if reboot is required.
|
||||||
|
func (pool *Pool) CreateAdapter(ifname string, requestedGUID *windows.GUID) (wintun *Adapter, rebootRequired bool, err error) {
|
||||||
|
var ifname16 *uint16
|
||||||
|
ifname16, err = windows.UTF16PtrFromString(ifname)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p0 uint32
|
||||||
|
r0, _, e1 := syscall.Syscall6(procWintunCreateAdapter.Addr(), 4, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), uintptr(unsafe.Pointer(requestedGUID)), uintptr(unsafe.Pointer(&_p0)), 0, 0)
|
||||||
|
rebootRequired = _p0 != 0
|
||||||
|
if r0 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wintun = &Adapter{r0}
|
||||||
|
runtime.SetFinalizer(wintun, freeAdapter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a Wintun adapter. This function succeeds if the adapter was not found. It returns
|
||||||
|
// a bool indicating whether a reboot is required.
|
||||||
|
func (wintun *Adapter) Delete(forceCloseSessions bool) (rebootRequired bool, err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if forceCloseSessions {
|
||||||
|
_p0 = 1
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunDeleteAdapter.Addr(), 3, uintptr(wintun.handle), uintptr(_p0), uintptr(unsafe.Pointer(&_p1)))
|
||||||
|
rebootRequired = _p1 != 0
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMatchingAdapters deletes all Wintun adapters, which match
|
||||||
|
// given criteria, and returns which ones it deleted, whether a reboot
|
||||||
|
// is required after, and which errors occurred during the process.
|
||||||
|
func (pool *Pool) DeleteMatchingAdapters(matches func(adapter *Adapter) bool, forceCloseSessions bool) (rebootRequired bool, errors []error) {
|
||||||
|
cb := func(handle uintptr, _ uintptr) int {
|
||||||
|
adapter := &Adapter{handle}
|
||||||
|
if !matches(adapter) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
rebootRequired2, err := adapter.Delete(forceCloseSessions)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
rebootRequired = rebootRequired || rebootRequired2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunEnumAdapters.Addr(), 3, uintptr(unsafe.Pointer(pool)), uintptr(windows.NewCallback(cb)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
errors = append(errors, e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the Wintun adapter.
|
||||||
|
func (wintun *Adapter) Name() (ifname string, err error) {
|
||||||
|
var ifname16 [AdapterNameMax]uint16
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunGetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ifname = windows.UTF16ToString(ifname16[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDriver deletes all Wintun adapters in a pool and if there are no more adapters in any other
|
||||||
|
// pools, also removes Wintun from the driver store, usually called by uninstallers.
|
||||||
|
func (pool *Pool) DeleteDriver() (rebootRequired bool, err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunDeletePoolDriver.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(&_p0)), 0)
|
||||||
|
rebootRequired = _p0 != 0
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName sets name of the Wintun adapter.
|
||||||
|
func (wintun *Adapter) SetName(ifname string) (err error) {
|
||||||
|
ifname16, err := windows.UTF16FromString(ifname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall(procWintunSetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunningVersion returns the version of the running Wintun driver.
|
||||||
|
func RunningVersion() (version uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0)
|
||||||
|
version = uint32(r0)
|
||||||
|
if version == 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LUID returns the LUID of the adapter.
|
||||||
|
func (wintun *Adapter) LUID() (luid uint64) {
|
||||||
|
syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0)
|
||||||
|
return
|
||||||
|
}
|
263
listener/tun/ipstack/gvisor/tun.go
Normal file
263
listener/tun/ipstack/gvisor/tun.go
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
package gvisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
"github.com/Dreamacro/clash/config"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/dns"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||||
|
"gvisor.dev/gvisor/pkg/waiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
const nicID tcpip.NICID = 1
|
||||||
|
|
||||||
|
type gvisorAdapter struct {
|
||||||
|
device dev.TunDevice
|
||||||
|
ipstack *stack.Stack
|
||||||
|
dnsserver *DNSServer
|
||||||
|
udpIn chan<- *inbound.PacketAdapter
|
||||||
|
|
||||||
|
stackName string
|
||||||
|
autoRoute bool
|
||||||
|
linkCache *channel.Endpoint
|
||||||
|
wg sync.WaitGroup // wait for goroutines to stop
|
||||||
|
|
||||||
|
writeHandle *channel.NotificationHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
// GvisorAdapter create GvisorAdapter
|
||||||
|
func NewAdapter(device dev.TunDevice, conf config.Tun, tunAddress string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
|
||||||
|
|
||||||
|
ipstack := stack.New(stack.Options{
|
||||||
|
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||||
|
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||||
|
})
|
||||||
|
|
||||||
|
adapter := &gvisorAdapter{
|
||||||
|
device: device,
|
||||||
|
ipstack: ipstack,
|
||||||
|
udpIn: udpIn,
|
||||||
|
stackName: conf.Stack,
|
||||||
|
autoRoute: conf.AutoRoute,
|
||||||
|
}
|
||||||
|
|
||||||
|
linkEP, err := adapter.AsLinkEndpoint()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create virtual endpoint: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipstack.CreateNIC(nicID, linkEP); err != nil {
|
||||||
|
return nil, fmt.Errorf("fail to create NIC in ipstack: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipstack.SetPromiscuousMode(nicID, true) // Accept all the traffice from this NIC
|
||||||
|
ipstack.SetSpoofing(nicID, true) // Otherwise our TCP connection can not find the route backward
|
||||||
|
|
||||||
|
// Add route for ipv4 & ipv6
|
||||||
|
// So FindRoute will return correct route to tun NIC
|
||||||
|
subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4)))
|
||||||
|
ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID})
|
||||||
|
subnet, _ = tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 6)), tcpip.AddressMask(strings.Repeat("\x00", 6)))
|
||||||
|
ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID})
|
||||||
|
|
||||||
|
// TCP handler
|
||||||
|
// maximum number of half-open tcp connection set to 1024
|
||||||
|
// receive buffer size set to 20k
|
||||||
|
tcpFwd := tcp.NewForwarder(ipstack, 20*1024, 1024, func(r *tcp.ForwarderRequest) {
|
||||||
|
var wq waiter.Queue
|
||||||
|
ep, err := r.CreateEndpoint(&wq)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("Can't create TCP Endpoint in ipstack: %v", err)
|
||||||
|
r.Complete(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Complete(false)
|
||||||
|
|
||||||
|
conn := gonet.NewTCPConn(&wq, ep)
|
||||||
|
|
||||||
|
// if the endpoint is not in connected state, conn.RemoteAddr() will return nil
|
||||||
|
// this protection may be not enough, but will help us debug the panic
|
||||||
|
if conn.RemoteAddr() == nil {
|
||||||
|
log.Warnln("TCP endpoint is not connected, current state: %v", tcp.EndpointState(ep.State()))
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
target := getAddr(ep.Info().(*stack.TransportEndpointInfo).ID)
|
||||||
|
tcpIn <- inbound.NewSocket(target, conn, C.TUN)
|
||||||
|
})
|
||||||
|
ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpFwd.HandlePacket)
|
||||||
|
|
||||||
|
// UDP handler
|
||||||
|
ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, adapter.udpHandlePacket)
|
||||||
|
|
||||||
|
if resolver.DefaultResolver != nil {
|
||||||
|
err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DNSListen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *gvisorAdapter) Stack() string {
|
||||||
|
return t.stackName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *gvisorAdapter) AutoRoute() bool {
|
||||||
|
return t.autoRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close close the TunAdapter
|
||||||
|
func (t *gvisorAdapter) Close() {
|
||||||
|
if t.dnsserver != nil {
|
||||||
|
t.dnsserver.Stop()
|
||||||
|
}
|
||||||
|
if t.ipstack != nil {
|
||||||
|
t.ipstack.Close()
|
||||||
|
}
|
||||||
|
if t.device != nil {
|
||||||
|
_ = t.device.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *gvisorAdapter) udpHandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||||
|
// ref: gvisor pkg/tcpip/transport/udp/endpoint.go HandlePacket
|
||||||
|
hdr := header.UDP(pkt.TransportHeader().View())
|
||||||
|
if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
|
||||||
|
// Malformed packet.
|
||||||
|
t.ipstack.Stats().UDP.MalformedPacketsReceived.Increment()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
target := getAddr(id)
|
||||||
|
|
||||||
|
packet := &fakeConn{
|
||||||
|
id: id,
|
||||||
|
pkt: pkt,
|
||||||
|
s: t.ipstack,
|
||||||
|
payload: pkt.Data().AsRange().ToOwnedView(),
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case t.udpIn <- inbound.NewPacket(target, packet, C.TUN):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait wait goroutines to exit
|
||||||
|
func (t *gvisorAdapter) Wait() {
|
||||||
|
t.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error) {
|
||||||
|
if t.linkCache != nil {
|
||||||
|
return t.linkCache, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu, err := t.device.MTU()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("unable to get device mtu")
|
||||||
|
}
|
||||||
|
|
||||||
|
linkEP := channel.New(512, uint32(mtu), "")
|
||||||
|
|
||||||
|
// start Read loop. read ip packet from tun and write it to ipstack
|
||||||
|
t.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
for !t.device.IsClose() {
|
||||||
|
packet := make([]byte, mtu)
|
||||||
|
n, err := t.device.Read(packet)
|
||||||
|
if err != nil && !t.device.IsClose() {
|
||||||
|
log.Errorln("can not read from tun: %v", err)
|
||||||
|
}
|
||||||
|
var p tcpip.NetworkProtocolNumber
|
||||||
|
switch header.IPVersion(packet) {
|
||||||
|
case header.IPv4Version:
|
||||||
|
p = header.IPv4ProtocolNumber
|
||||||
|
case header.IPv6Version:
|
||||||
|
p = header.IPv6ProtocolNumber
|
||||||
|
}
|
||||||
|
if linkEP.IsAttached() {
|
||||||
|
linkEP.InjectInbound(p, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
Data: buffer.View(packet[:n]).ToVectorisedView(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
log.Debugln("received packet from tun when %s is not attached to any dispatcher.", t.device.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.wg.Done()
|
||||||
|
t.Close()
|
||||||
|
log.Debugln("%v stop read loop", t.device.Name())
|
||||||
|
}()
|
||||||
|
|
||||||
|
// start write notification
|
||||||
|
t.writeHandle = linkEP.AddNotify(t)
|
||||||
|
t.linkCache = linkEP
|
||||||
|
return t.linkCache, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteNotify implements channel.Notification.WriteNotify.
|
||||||
|
func (t *gvisorAdapter) WriteNotify() {
|
||||||
|
packet, ok := t.linkCache.Read()
|
||||||
|
if ok {
|
||||||
|
var vv buffer.VectorisedView
|
||||||
|
// Append upper headers.
|
||||||
|
vv.AppendView(packet.Pkt.NetworkHeader().View())
|
||||||
|
vv.AppendView(packet.Pkt.TransportHeader().View())
|
||||||
|
// Append data payload.
|
||||||
|
vv.Append(packet.Pkt.Data().ExtractVV())
|
||||||
|
|
||||||
|
_, err := t.device.Write(vv.ToView())
|
||||||
|
if err != nil && !t.device.IsClose() {
|
||||||
|
log.Errorln("can not write to tun: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAddr(id stack.TransportEndpointID) socks5.Addr {
|
||||||
|
ipv4 := id.LocalAddress.To4()
|
||||||
|
|
||||||
|
// get the big-endian binary represent of port
|
||||||
|
port := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(port, id.LocalPort)
|
||||||
|
|
||||||
|
if ipv4 != "" {
|
||||||
|
addr := make([]byte, 1+net.IPv4len+2)
|
||||||
|
addr[0] = socks5.AtypIPv4
|
||||||
|
copy(addr[1:1+net.IPv4len], []byte(ipv4))
|
||||||
|
addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
|
||||||
|
return addr
|
||||||
|
} else {
|
||||||
|
addr := make([]byte, 1+net.IPv6len+2)
|
||||||
|
addr[0] = socks5.AtypIPv6
|
||||||
|
copy(addr[1:1+net.IPv6len], []byte(id.LocalAddress))
|
||||||
|
addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1]
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
}
|
280
listener/tun/ipstack/gvisor/tundns.go
Normal file
280
listener/tun/ipstack/gvisor/tundns.go
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
package gvisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/dns"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
D "github.com/miekg/dns"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ipv4Zero = tcpip.Address(net.IPv4zero.To4())
|
||||||
|
ipv6Zero = tcpip.Address(net.IPv6zero.To16())
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSServer is DNS Server listening on tun devcice
|
||||||
|
type DNSServer struct {
|
||||||
|
*dns.Server
|
||||||
|
resolver *dns.Resolver
|
||||||
|
|
||||||
|
stack *stack.Stack
|
||||||
|
tcpListener net.Listener
|
||||||
|
udpEndpoint *dnsEndpoint
|
||||||
|
udpEndpointID *stack.TransportEndpointID
|
||||||
|
tcpip.NICID
|
||||||
|
}
|
||||||
|
|
||||||
|
// dnsEndpoint is a TransportEndpoint that will register to stack
|
||||||
|
type dnsEndpoint struct {
|
||||||
|
stack.TransportEndpoint
|
||||||
|
stack *stack.Stack
|
||||||
|
uniqueID uint64
|
||||||
|
server *dns.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the source of DNS request
|
||||||
|
type dnsResponseWriter struct {
|
||||||
|
s *stack.Stack
|
||||||
|
pkt *stack.PacketBuffer // The request packet
|
||||||
|
id stack.TransportEndpointID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *dnsEndpoint) UniqueID() uint64 {
|
||||||
|
return e.uniqueID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *dnsEndpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) {
|
||||||
|
hdr := header.UDP(pkt.TransportHeader().View())
|
||||||
|
if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
|
||||||
|
// Malformed packet.
|
||||||
|
e.stack.Stats().UDP.MalformedPacketsReceived.Increment()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// server DNS
|
||||||
|
var msg D.Msg
|
||||||
|
msg.Unpack(pkt.Data().AsRange().ToOwnedView())
|
||||||
|
writer := dnsResponseWriter{s: e.stack, pkt: pkt, id: id}
|
||||||
|
go e.server.ServeDNS(&writer, &msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *dnsEndpoint) Close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *dnsEndpoint) Wait() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
|
||||||
|
log.Warnln("DNS endpoint get a transport error: %v", transErr)
|
||||||
|
log.Debugln("DNS endpoint transport error packet : %v", pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort implements stack.TransportEndpoint.Abort.
|
||||||
|
func (e *dnsEndpoint) Abort() {
|
||||||
|
e.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{IP: net.IP(w.id.LocalAddress), Port: int(w.id.LocalPort)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) RemoteAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{IP: net.IP(w.id.RemoteAddress), Port: int(w.id.RemotePort)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) WriteMsg(msg *D.Msg) error {
|
||||||
|
b, err := msg.Pack()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (w *dnsResponseWriter) TsigStatus() error {
|
||||||
|
// Unsupported
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (w *dnsResponseWriter) TsigTimersOnly(bool) {
|
||||||
|
// Unsupported
|
||||||
|
}
|
||||||
|
func (w *dnsResponseWriter) Hijack() {
|
||||||
|
// Unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
v := buffer.NewView(len(b))
|
||||||
|
copy(v, b)
|
||||||
|
data := v.ToVectorisedView()
|
||||||
|
// w.id.LocalAddress is the source ip of DNS response
|
||||||
|
r, _ := w.s.FindRoute(w.pkt.NICID, w.id.LocalAddress, w.id.RemoteAddress, w.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||||
|
return writeUDP(r, data, w.id.LocalPort, w.id.RemotePort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *dnsResponseWriter) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDNSServer create a dns server on given netstack
|
||||||
|
func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, ip net.IP, port int, nicID tcpip.NICID) (*DNSServer, error) {
|
||||||
|
|
||||||
|
var v4 bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
address := tcpip.FullAddress{NIC: nicID, Port: uint16(port)}
|
||||||
|
if ip.To4() != nil {
|
||||||
|
v4 = true
|
||||||
|
address.Addr = tcpip.Address(ip.To4())
|
||||||
|
// netstack will only reassemble IP fragments when its' dest ip address is registered in NIC.endpoints
|
||||||
|
s.AddAddress(nicID, ipv4.ProtocolNumber, address.Addr)
|
||||||
|
} else {
|
||||||
|
v4 = false
|
||||||
|
address.Addr = tcpip.Address(ip.To16())
|
||||||
|
s.AddAddress(nicID, ipv6.ProtocolNumber, address.Addr)
|
||||||
|
}
|
||||||
|
if address.Addr == ipv4Zero || address.Addr == ipv6Zero {
|
||||||
|
address.Addr = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := dns.NewHandler(resolver, mapper)
|
||||||
|
serverIn := &dns.Server{}
|
||||||
|
serverIn.SetHandler(handler)
|
||||||
|
|
||||||
|
// UDP DNS
|
||||||
|
id := &stack.TransportEndpointID{
|
||||||
|
LocalAddress: address.Addr,
|
||||||
|
LocalPort: uint16(port),
|
||||||
|
RemotePort: 0,
|
||||||
|
RemoteAddress: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransportEndpoint for DNS
|
||||||
|
endpoint := &dnsEndpoint{
|
||||||
|
stack: s,
|
||||||
|
uniqueID: s.UniqueID(),
|
||||||
|
server: serverIn,
|
||||||
|
}
|
||||||
|
|
||||||
|
if tcpiperr := s.RegisterTransportEndpoint(
|
||||||
|
[]tcpip.NetworkProtocolNumber{
|
||||||
|
ipv4.ProtocolNumber,
|
||||||
|
ipv6.ProtocolNumber,
|
||||||
|
},
|
||||||
|
udp.ProtocolNumber,
|
||||||
|
*id,
|
||||||
|
endpoint,
|
||||||
|
ports.Flags{LoadBalanced: true}, // it's actually the SO_REUSEPORT. Not sure it take effect.
|
||||||
|
nicID); err != nil {
|
||||||
|
log.Errorln("Unable to start UDP DNS on tun: %v", tcpiperr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP DNS
|
||||||
|
var tcpListener net.Listener
|
||||||
|
if v4 {
|
||||||
|
tcpListener, err = gonet.ListenTCP(s, address, ipv4.ProtocolNumber)
|
||||||
|
} else {
|
||||||
|
tcpListener, err = gonet.ListenTCP(s, address, ipv6.ProtocolNumber)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can not listen on tun: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &DNSServer{
|
||||||
|
Server: serverIn,
|
||||||
|
resolver: resolver,
|
||||||
|
stack: s,
|
||||||
|
tcpListener: tcpListener,
|
||||||
|
udpEndpoint: endpoint,
|
||||||
|
udpEndpointID: id,
|
||||||
|
NICID: nicID,
|
||||||
|
}
|
||||||
|
server.SetHandler(handler)
|
||||||
|
server.Server.Server = &D.Server{Listener: tcpListener, Handler: server}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ActivateAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return server, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stop the DNS Server on tun
|
||||||
|
func (s *DNSServer) Stop() {
|
||||||
|
// shutdown TCP DNS Server
|
||||||
|
s.Server.Shutdown()
|
||||||
|
// remove TCP endpoint from stack
|
||||||
|
if s.Listener != nil {
|
||||||
|
s.Listener.Close()
|
||||||
|
}
|
||||||
|
// remove udp endpoint from stack
|
||||||
|
s.stack.UnregisterTransportEndpoint(
|
||||||
|
[]tcpip.NetworkProtocolNumber{
|
||||||
|
ipv4.ProtocolNumber,
|
||||||
|
ipv6.ProtocolNumber,
|
||||||
|
},
|
||||||
|
udp.ProtocolNumber,
|
||||||
|
*s.udpEndpointID,
|
||||||
|
s.udpEndpoint,
|
||||||
|
ports.Flags{LoadBalanced: true}, // should match the RegisterTransportEndpoint
|
||||||
|
s.NICID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSListen return the listening address of DNS Server
|
||||||
|
func (t *gvisorAdapter) DNSListen() string {
|
||||||
|
if t.dnsserver != nil {
|
||||||
|
id := t.dnsserver.udpEndpointID
|
||||||
|
return fmt.Sprintf("%s:%d", id.LocalAddress.String(), id.LocalPort)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stop the DNS Server on tun
|
||||||
|
func (t *gvisorAdapter) ReCreateDNSServer(resolver *dns.Resolver, mapper *dns.ResolverEnhancer, addr string) error {
|
||||||
|
if addr == "" && t.dnsserver == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr == t.DNSListen() && t.dnsserver != nil && t.dnsserver.resolver == resolver {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.dnsserver != nil {
|
||||||
|
t.dnsserver.Stop()
|
||||||
|
t.dnsserver = nil
|
||||||
|
log.Debugln("tun DNS server stoped")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
_, port, err := net.SplitHostPort(addr)
|
||||||
|
if port == "0" || port == "" || err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if resolver == nil {
|
||||||
|
return fmt.Errorf("failed to create DNS server on tun: resolver not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := CreateDNSServer(t.ipstack, resolver, mapper, udpAddr.IP, udpAddr.Port, nicID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.dnsserver = server
|
||||||
|
log.Infoln("Tun DNS server listening at: %s, fake ip enabled: %v", addr, mapper.FakeIPEnabled())
|
||||||
|
return nil
|
||||||
|
}
|
109
listener/tun/ipstack/gvisor/utils.go
Normal file
109
listener/tun/ipstack/gvisor/utils.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package gvisor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeConn struct {
|
||||||
|
id stack.TransportEndpointID // The endpoint of incomming packet, it's remote address is the source address it sent from
|
||||||
|
pkt *stack.PacketBuffer // The original packet comming from tun
|
||||||
|
s *stack.Stack
|
||||||
|
payload []byte
|
||||||
|
fakeip *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) Data() []byte {
|
||||||
|
return c.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
|
v := buffer.View(b)
|
||||||
|
data := v.ToVectorisedView()
|
||||||
|
|
||||||
|
var localAddress tcpip.Address
|
||||||
|
var localPort uint16
|
||||||
|
// if addr is not provided, write back use original dst Addr as src Addr
|
||||||
|
if c.FakeIP() || addr == nil {
|
||||||
|
localAddress = c.id.LocalAddress
|
||||||
|
localPort = c.id.LocalPort
|
||||||
|
} else {
|
||||||
|
udpaddr, _ := addr.(*net.UDPAddr)
|
||||||
|
localAddress = tcpip.Address(udpaddr.IP)
|
||||||
|
localPort = uint16(udpaddr.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _ := c.s.FindRoute(c.pkt.NICID, localAddress, c.id.RemoteAddress, c.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||||
|
return writeUDP(r, data, localPort, c.id.RemotePort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{IP: net.IP(c.id.RemoteAddress), Port: int(c.id.RemotePort)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) Drop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeConn) FakeIP() bool {
|
||||||
|
if c.fakeip != nil {
|
||||||
|
return *c.fakeip
|
||||||
|
}
|
||||||
|
fakeip := resolver.IsFakeIP(net.IP(c.id.LocalAddress.To4()))
|
||||||
|
c.fakeip = &fakeip
|
||||||
|
return fakeip
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16) (int, error) {
|
||||||
|
const protocol = udp.ProtocolNumber
|
||||||
|
// Allocate a buffer for the UDP header.
|
||||||
|
|
||||||
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Initialize the header.
|
||||||
|
udp := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
|
||||||
|
|
||||||
|
length := uint16(pkt.Size())
|
||||||
|
udp.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() {
|
||||||
|
xsum := r.PseudoHeaderChecksum(protocol, length)
|
||||||
|
for _, v := range data.Views() {
|
||||||
|
xsum = header.Checksum(v, xsum)
|
||||||
|
}
|
||||||
|
udp.SetChecksum(^udp.CalculateChecksum(xsum))
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl := r.DefaultTTL()
|
||||||
|
|
||||||
|
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: protocol, TTL: ttl, TOS: 0 /* default */}, pkt); err != nil {
|
||||||
|
r.Stats().UDP.PacketSendErrors.Increment()
|
||||||
|
return 0, fmt.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track count of packets sent.
|
||||||
|
r.Stats().UDP.PacketsSent.Increment()
|
||||||
|
return data.Size(), nil
|
||||||
|
}
|
9
listener/tun/ipstack/stack_adapter.go
Normal file
9
listener/tun/ipstack/stack_adapter.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package ipstack
|
||||||
|
|
||||||
|
// TunAdapter hold the state of tun/tap interface
|
||||||
|
type TunAdapter interface {
|
||||||
|
Close()
|
||||||
|
Stack() string
|
||||||
|
DNSListen() string
|
||||||
|
AutoRoute() bool
|
||||||
|
}
|
101
listener/tun/ipstack/system/dns.go
Normal file
101
listener/tun/ipstack/system/dns.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
|
||||||
|
D "github.com/miekg/dns"
|
||||||
|
|
||||||
|
"github.com/kr328/tun2socket/binding"
|
||||||
|
"github.com/kr328/tun2socket/redirect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultDnsReadTimeout = time.Second * 30
|
||||||
|
|
||||||
|
func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool {
|
||||||
|
if targetAddr.Port != 53 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
|
||||||
|
go func() {
|
||||||
|
answer, err := relayDnsPacket(pkt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sender(answer, &binding.Endpoint{
|
||||||
|
Source: ep.Target,
|
||||||
|
Target: ep.Source,
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hijackTCPDns(conn net.Conn) {
|
||||||
|
go func() {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var length uint16
|
||||||
|
if binary.Read(conn, binary.BigEndian, &length) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, length)
|
||||||
|
|
||||||
|
_, err := io.ReadFull(conn, data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rb, err := relayDnsPacket(data)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := conn.Write(rb); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func relayDnsPacket(payload []byte) ([]byte, error) {
|
||||||
|
msg := &D.Msg{}
|
||||||
|
if err := msg.Unpack(payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := resolver.ServeMsg(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ans := range r.Answer {
|
||||||
|
header := ans.Header()
|
||||||
|
|
||||||
|
if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) {
|
||||||
|
header.Ttl = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.SetRcode(msg, r.Rcode)
|
||||||
|
r.Compress = true
|
||||||
|
return r.Pack()
|
||||||
|
}
|
21
listener/tun/ipstack/system/log.go
Normal file
21
listener/tun/ipstack/system/log.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
|
type logger struct{}
|
||||||
|
|
||||||
|
func (l *logger) D(format string, args ...interface{}) {
|
||||||
|
log.Debugln("[TUN] "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) I(format string, args ...interface{}) {
|
||||||
|
log.Infoln("[TUN] "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) W(format string, args ...interface{}) {
|
||||||
|
log.Warnln("[TUN] "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) E(format string, args ...interface{}) {
|
||||||
|
log.Errorln("[TUN] "+format, args...)
|
||||||
|
}
|
37
listener/tun/ipstack/system/tcp.go
Normal file
37
listener/tun/ipstack/system/tcp.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/kr328/tun2socket/binding"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleTCP(conn net.Conn, endpoint *binding.Endpoint, tcpIn chan<- C.ConnContext) {
|
||||||
|
src := &net.TCPAddr{
|
||||||
|
IP: endpoint.Source.IP,
|
||||||
|
Port: int(endpoint.Source.Port),
|
||||||
|
Zone: "",
|
||||||
|
}
|
||||||
|
dst := &net.TCPAddr{
|
||||||
|
IP: endpoint.Target.IP,
|
||||||
|
Port: int(endpoint.Target.Port),
|
||||||
|
Zone: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := &C.Metadata{
|
||||||
|
NetWork: C.TCP,
|
||||||
|
Type: C.TUN,
|
||||||
|
SrcIP: src.IP,
|
||||||
|
DstIP: dst.IP,
|
||||||
|
SrcPort: strconv.Itoa(src.Port),
|
||||||
|
DstPort: strconv.Itoa(dst.Port),
|
||||||
|
AddrType: C.AtypIPv4,
|
||||||
|
Host: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpIn <- context.NewConnContext(conn, metadata)
|
||||||
|
}
|
125
listener/tun/ipstack/system/tun.go
Normal file
125
listener/tun/ipstack/system/tun.go
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/config"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/kr328/tun2socket"
|
||||||
|
"github.com/kr328/tun2socket/binding"
|
||||||
|
"github.com/kr328/tun2socket/redirect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type systemAdapter struct {
|
||||||
|
device dev.TunDevice
|
||||||
|
tun *tun2socket.Tun2Socket
|
||||||
|
lock sync.Mutex
|
||||||
|
stackName string
|
||||||
|
dnsListen string
|
||||||
|
autoRoute bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror string, onStop func(), tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
|
||||||
|
|
||||||
|
adapter := &systemAdapter{
|
||||||
|
device: device,
|
||||||
|
stackName: conf.Stack,
|
||||||
|
dnsListen: conf.DNSListen,
|
||||||
|
autoRoute: conf.AutoRoute,
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.lock.Lock()
|
||||||
|
defer adapter.lock.Unlock()
|
||||||
|
|
||||||
|
//adapter.stopLocked()
|
||||||
|
|
||||||
|
dnsHost, dnsPort, err := net.SplitHostPort(conf.DNSListen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsP, err := strconv.Atoi(dnsPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsAddr := binding.Address{
|
||||||
|
IP: net.ParseIP(dnsHost),
|
||||||
|
Port: uint16(dnsP),
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tun2socket.NewTun2Socket(device, mtu, net.ParseIP(gateway), net.ParseIP(mirror))
|
||||||
|
|
||||||
|
t.SetAllocator(allocUDP)
|
||||||
|
t.SetClosedHandler(onStop)
|
||||||
|
t.SetLogger(&logger{})
|
||||||
|
|
||||||
|
t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
|
||||||
|
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
||||||
|
hijackTCPDns(conn)
|
||||||
|
|
||||||
|
if log.Level() == log.DEBUG {
|
||||||
|
log.Debugln("[TUN] hijack dns tcp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTCP(conn, endpoint, tcpIn)
|
||||||
|
})
|
||||||
|
t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
||||||
|
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
||||||
|
hijackUDPDns(payload, endpoint, sender)
|
||||||
|
|
||||||
|
if log.Level() == log.DEBUG {
|
||||||
|
log.Debugln("[TUN] hijack dns udp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUDP(payload, endpoint, sender, udpIn)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Start()
|
||||||
|
|
||||||
|
adapter.tun = t
|
||||||
|
|
||||||
|
return adapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *systemAdapter) Stack() string {
|
||||||
|
return t.stackName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *systemAdapter) AutoRoute() bool {
|
||||||
|
return t.autoRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *systemAdapter) DNSListen() string {
|
||||||
|
return t.dnsListen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *systemAdapter) Close() {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
t.stopLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *systemAdapter) stopLocked() {
|
||||||
|
if t.tun != nil {
|
||||||
|
t.tun.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
_ = t.device.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.tun = nil
|
||||||
|
t.device = nil
|
||||||
|
}
|
74
listener/tun/ipstack/system/udp.go
Normal file
74
listener/tun/ipstack/system/udp.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
|
"github.com/kr328/tun2socket/binding"
|
||||||
|
"github.com/kr328/tun2socket/redirect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type udpPacket struct {
|
||||||
|
source binding.Address
|
||||||
|
data []byte
|
||||||
|
send redirect.UDPSender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpPacket) Data() []byte {
|
||||||
|
return u.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
|
uAddr, ok := addr.(*net.UDPAddr)
|
||||||
|
if !ok {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(b), u.send(b, &binding.Endpoint{
|
||||||
|
Source: binding.Address{IP: uAddr.IP, Port: uint16(uAddr.Port)},
|
||||||
|
Target: u.source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpPacket) Drop() {
|
||||||
|
recycleUDP(u.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpPacket) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: u.source.IP,
|
||||||
|
Port: int(u.source.Port),
|
||||||
|
Zone: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUDP(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender, udpIn chan<- *inbound.PacketAdapter) {
|
||||||
|
pkt := &udpPacket{
|
||||||
|
source: endpoint.Source,
|
||||||
|
data: payload,
|
||||||
|
send: sender,
|
||||||
|
}
|
||||||
|
|
||||||
|
rAddr := &net.UDPAddr{
|
||||||
|
IP: endpoint.Target.IP,
|
||||||
|
Port: int(endpoint.Target.Port),
|
||||||
|
Zone: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allocUDP(size int) []byte {
|
||||||
|
return pool.Get(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func recycleUDP(payload []byte) {
|
||||||
|
_ = pool.Put(payload)
|
||||||
|
}
|
51
listener/tun/tun_adapter.go
Normal file
51
listener/tun/tun_adapter.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
"github.com/Dreamacro/clash/config"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/dev"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
|
||||||
|
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New create TunAdapter
|
||||||
|
func New(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
|
||||||
|
tunAddress := "198.18.0.1"
|
||||||
|
autoRoute := conf.AutoRoute
|
||||||
|
stack := conf.Stack
|
||||||
|
var tunAdapter ipstack.TunAdapter
|
||||||
|
|
||||||
|
device, err := dev.OpenTunDevice(tunAddress, autoRoute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't open tun: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu, err := device.MTU()
|
||||||
|
if err != nil {
|
||||||
|
_ = device.Close()
|
||||||
|
return nil, errors.New("unable to get device mtu")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(stack, "system") {
|
||||||
|
tunAdapter, err = system.NewAdapter(device, conf, mtu, tunAddress, tunAddress, func() {}, tcpIn, udpIn)
|
||||||
|
} else if strings.EqualFold(stack, "gvisor") {
|
||||||
|
tunAdapter, err = gvisor.NewAdapter(device, conf, tunAddress, tcpIn, udpIn)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("can not support tun ip stack: %s, only support \"system\" and \"gvisor\"", stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_ = device.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("Tun adapter listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", device.Name(), tunAddress, mtu, autoRoute, stack)
|
||||||
|
return tunAdapter, nil
|
||||||
|
}
|
|
@ -97,3 +97,9 @@ func newLog(logLevel LogLevel, format string, v ...interface{}) *Event {
|
||||||
Payload: fmt.Sprintf(format, v...),
|
Payload: fmt.Sprintf(format, v...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PrintLog(logLevel LogLevel, format string, v ...interface{}) {
|
||||||
|
event := newLog(logLevel, format, v...)
|
||||||
|
logCh <- event
|
||||||
|
print(event)
|
||||||
|
}
|
||||||
|
|
8
main.go
8
main.go
|
@ -98,6 +98,12 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sigCh
|
<-sigCh
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
log.Warnln("Clash clean up")
|
||||||
|
hub.CleanUp()
|
||||||
|
|
||||||
|
log.Warnln("Clash shutting down")
|
||||||
}
|
}
|
||||||
|
|
13
rule/base.go
13
rule/base.go
|
@ -2,6 +2,8 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -18,3 +20,14 @@ func HasNoResolve(params []string) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findNetwork(params []string) C.NetWork {
|
||||||
|
for _, p := range params {
|
||||||
|
if p == "tcp" {
|
||||||
|
return C.TCP
|
||||||
|
} else if p == "udp" {
|
||||||
|
return C.UDP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return C.ALLNet
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type Domain struct {
|
type Domain struct {
|
||||||
domain string
|
domain string
|
||||||
adapter string
|
adapter string
|
||||||
|
network C.NetWork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Domain) RuleType() C.RuleType {
|
func (d *Domain) RuleType() C.RuleType {
|
||||||
|
@ -34,9 +35,14 @@ func (d *Domain) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomain(domain string, adapter string) *Domain {
|
func (d *Domain) NetWork() C.NetWork {
|
||||||
|
return d.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDomain(domain string, adapter string, network C.NetWork) *Domain {
|
||||||
return &Domain{
|
return &Domain{
|
||||||
domain: strings.ToLower(domain),
|
domain: strings.ToLower(domain),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
network: network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type DomainKeyword struct {
|
type DomainKeyword struct {
|
||||||
keyword string
|
keyword string
|
||||||
adapter string
|
adapter string
|
||||||
|
network C.NetWork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dk *DomainKeyword) RuleType() C.RuleType {
|
func (dk *DomainKeyword) RuleType() C.RuleType {
|
||||||
|
@ -35,9 +36,14 @@ func (dk *DomainKeyword) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
func (dk *DomainKeyword) NetWork() C.NetWork {
|
||||||
|
return dk.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDomainKeyword(keyword string, adapter string, network C.NetWork) *DomainKeyword {
|
||||||
return &DomainKeyword{
|
return &DomainKeyword{
|
||||||
keyword: strings.ToLower(keyword),
|
keyword: strings.ToLower(keyword),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
network: network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type DomainSuffix struct {
|
type DomainSuffix struct {
|
||||||
suffix string
|
suffix string
|
||||||
adapter string
|
adapter string
|
||||||
|
network C.NetWork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *DomainSuffix) RuleType() C.RuleType {
|
func (ds *DomainSuffix) RuleType() C.RuleType {
|
||||||
|
@ -35,9 +36,14 @@ func (ds *DomainSuffix) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
|
func (ds *DomainSuffix) NetWork() C.NetWork {
|
||||||
|
return ds.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDomainSuffix(suffix string, adapter string, network C.NetWork) *DomainSuffix {
|
||||||
return &DomainSuffix{
|
return &DomainSuffix{
|
||||||
suffix: strings.ToLower(suffix),
|
suffix: strings.ToLower(suffix),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
network: network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ func (f *Match) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Match) NetWork() C.NetWork {
|
||||||
|
return C.ALLNet
|
||||||
|
}
|
||||||
|
|
||||||
func NewMatch(adapter string) *Match {
|
func NewMatch(adapter string) *Match {
|
||||||
return &Match{
|
return &Match{
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
|
51
rule/geodata/attr.go
Normal file
51
rule/geodata/attr.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package geodata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AttributeList struct {
|
||||||
|
matcher []AttributeMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AttributeList) Match(domain *router.Domain) bool {
|
||||||
|
for _, matcher := range al.matcher {
|
||||||
|
if !matcher.Match(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AttributeList) IsEmpty() bool {
|
||||||
|
return len(al.matcher) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAttrs(attrs []string) *AttributeList {
|
||||||
|
al := new(AttributeList)
|
||||||
|
for _, attr := range attrs {
|
||||||
|
trimmedAttr := strings.ToLower(strings.TrimSpace(attr))
|
||||||
|
if len(trimmedAttr) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
al.matcher = append(al.matcher, BooleanMatcher(trimmedAttr))
|
||||||
|
}
|
||||||
|
return al
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeMatcher interface {
|
||||||
|
Match(*router.Domain) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BooleanMatcher string
|
||||||
|
|
||||||
|
func (m BooleanMatcher) Match(domain *router.Domain) bool {
|
||||||
|
for _, attr := range domain.Attribute {
|
||||||
|
if strings.EqualFold(attr.GetKey(), string(m)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
86
rule/geodata/geodata.go
Normal file
86
rule/geodata/geodata.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package geodata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
type loader struct {
|
||||||
|
LoaderImplementation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) LoadGeoSite(list string) ([]*router.Domain, error) {
|
||||||
|
return l.LoadGeoSiteWithAttr("geosite.dat", list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
|
||||||
|
parts := strings.Split(siteWithAttr, "@")
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return nil, errors.New("empty rule")
|
||||||
|
}
|
||||||
|
list := strings.TrimSpace(parts[0])
|
||||||
|
attrVal := parts[1:]
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
domains, err := l.LoadSite(file, list)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := parseAttrs(attrVal)
|
||||||
|
if attrs.IsEmpty() {
|
||||||
|
if strings.Contains(siteWithAttr, "@") {
|
||||||
|
log.Warnln("empty attribute list: %s", siteWithAttr)
|
||||||
|
}
|
||||||
|
return domains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredDomains := make([]*router.Domain, 0, len(domains))
|
||||||
|
hasAttrMatched := false
|
||||||
|
for _, domain := range domains {
|
||||||
|
if attrs.Match(domain) {
|
||||||
|
hasAttrMatched = true
|
||||||
|
filteredDomains = append(filteredDomains, domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasAttrMatched {
|
||||||
|
log.Warnln("attribute match no rule: geosite: %s", siteWithAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredDomains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {
|
||||||
|
return l.LoadIP("geoip.dat", country)
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaders map[string]func() LoaderImplementation
|
||||||
|
|
||||||
|
func RegisterGeoDataLoaderImplementationCreator(name string, loader func() LoaderImplementation) {
|
||||||
|
if loaders == nil {
|
||||||
|
loaders = map[string]func() LoaderImplementation{}
|
||||||
|
}
|
||||||
|
loaders[name] = loader
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGeoDataLoaderImplementation(name string) (LoaderImplementation, error) {
|
||||||
|
if geoLoader, ok := loaders[name]; ok {
|
||||||
|
return geoLoader(), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unable to locate GeoData loader %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGeoDataLoader(name string) (Loader, error) {
|
||||||
|
loadImpl, err := getGeoDataLoaderImplementation(name)
|
||||||
|
if err == nil {
|
||||||
|
return &loader{loadImpl}, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
15
rule/geodata/geodataproto.go
Normal file
15
rule/geodata/geodataproto.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package geodata
|
||||||
|
|
||||||
|
import "github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
|
||||||
|
type LoaderImplementation interface {
|
||||||
|
LoadSite(filename, list string) ([]*router.Domain, error)
|
||||||
|
LoadIP(filename, country string) ([]*router.CIDR, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Loader interface {
|
||||||
|
LoaderImplementation
|
||||||
|
LoadGeoSite(list string) ([]*router.Domain, error)
|
||||||
|
LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error)
|
||||||
|
LoadGeoIP(country string) ([]*router.CIDR, error)
|
||||||
|
}
|
142
rule/geodata/memconservative/cache.go
Normal file
142
rule/geodata/memconservative/cache.go
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package memconservative
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GeoIPCache map[string]*router.GeoIP
|
||||||
|
|
||||||
|
func (g GeoIPCache) Has(key string) bool {
|
||||||
|
return !(g.Get(key) == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoIPCache) Get(key string) *router.GeoIP {
|
||||||
|
if g == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoIPCache) Set(key string, value *router.GeoIP) {
|
||||||
|
if g == nil {
|
||||||
|
g = make(map[string]*router.GeoIP)
|
||||||
|
}
|
||||||
|
g[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoIPCache) Unmarshal(filename, code string) (*router.GeoIP, error) {
|
||||||
|
asset := C.Path.GetAssetLocation(filename)
|
||||||
|
idx := strings.ToLower(asset + ":" + code)
|
||||||
|
if g.Has(idx) {
|
||||||
|
return g.Get(idx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
geoipBytes, err := Decode(asset, code)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
var geoip router.GeoIP
|
||||||
|
if err := proto.Unmarshal(geoipBytes, &geoip); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g.Set(idx, &geoip)
|
||||||
|
return &geoip, nil
|
||||||
|
|
||||||
|
case errCodeNotFound:
|
||||||
|
return nil, fmt.Errorf("country code %s%s%s", code, " not found in ", filename)
|
||||||
|
|
||||||
|
case errFailedToReadBytes, errFailedToReadExpectedLenBytes,
|
||||||
|
errInvalidGeodataFile, errInvalidGeodataVarintLength:
|
||||||
|
log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method")
|
||||||
|
geoipBytes, err = ioutil.ReadFile(asset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var geoipList router.GeoIPList
|
||||||
|
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, geoip := range geoipList.GetEntry() {
|
||||||
|
if strings.EqualFold(code, geoip.GetCountryCode()) {
|
||||||
|
g.Set(idx, geoip)
|
||||||
|
return geoip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("country code %s%s%s", code, " not found in ", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSiteCache map[string]*router.GeoSite
|
||||||
|
|
||||||
|
func (g GeoSiteCache) Has(key string) bool {
|
||||||
|
return !(g.Get(key) == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoSiteCache) Get(key string) *router.GeoSite {
|
||||||
|
if g == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoSiteCache) Set(key string, value *router.GeoSite) {
|
||||||
|
if g == nil {
|
||||||
|
g = make(map[string]*router.GeoSite)
|
||||||
|
}
|
||||||
|
g[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error) {
|
||||||
|
asset := C.Path.GetAssetLocation(filename)
|
||||||
|
idx := strings.ToLower(asset + ":" + code)
|
||||||
|
if g.Has(idx) {
|
||||||
|
return g.Get(idx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
geositeBytes, err := Decode(asset, code)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
var geosite router.GeoSite
|
||||||
|
if err := proto.Unmarshal(geositeBytes, &geosite); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g.Set(idx, &geosite)
|
||||||
|
return &geosite, nil
|
||||||
|
|
||||||
|
case errCodeNotFound:
|
||||||
|
return nil, fmt.Errorf("list %s%s%s", code, " not found in ", filename)
|
||||||
|
|
||||||
|
case errFailedToReadBytes, errFailedToReadExpectedLenBytes,
|
||||||
|
errInvalidGeodataFile, errInvalidGeodataVarintLength:
|
||||||
|
log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method")
|
||||||
|
geositeBytes, err = ioutil.ReadFile(asset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var geositeList router.GeoSiteList
|
||||||
|
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, geosite := range geositeList.GetEntry() {
|
||||||
|
if strings.EqualFold(code, geosite.GetCountryCode()) {
|
||||||
|
g.Set(idx, geosite)
|
||||||
|
return geosite, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("list %s%s%s", code, " not found in ", filename)
|
||||||
|
}
|
105
rule/geodata/memconservative/decode.go
Normal file
105
rule/geodata/memconservative/decode.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package memconservative
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFailedToReadBytes = errors.New("failed to read bytes")
|
||||||
|
errFailedToReadExpectedLenBytes = errors.New("failed to read expected length of bytes")
|
||||||
|
errInvalidGeodataFile = errors.New("invalid geodata file")
|
||||||
|
errInvalidGeodataVarintLength = errors.New("invalid geodata varint length")
|
||||||
|
errCodeNotFound = errors.New("code not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
func emitBytes(f io.ReadSeeker, code string) ([]byte, error) {
|
||||||
|
count := 1
|
||||||
|
isInner := false
|
||||||
|
tempContainer := make([]byte, 0, 5)
|
||||||
|
|
||||||
|
var result []byte
|
||||||
|
var advancedN uint64 = 1
|
||||||
|
var geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
container := make([]byte, advancedN)
|
||||||
|
bytesRead, err := f.Read(container)
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, errCodeNotFound
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errFailedToReadBytes
|
||||||
|
}
|
||||||
|
if bytesRead != len(container) {
|
||||||
|
return nil, errFailedToReadExpectedLenBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 1, 3: // data type ((field_number << 3) | wire_type)
|
||||||
|
if container[0] != 10 { // byte `0A` equals to `10` in decimal
|
||||||
|
return nil, errInvalidGeodataFile
|
||||||
|
}
|
||||||
|
advancedN = 1
|
||||||
|
count++
|
||||||
|
case 2, 4: // data length
|
||||||
|
tempContainer = append(tempContainer, container...)
|
||||||
|
if container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal
|
||||||
|
advancedN = 1
|
||||||
|
goto Loop
|
||||||
|
}
|
||||||
|
lenVarint, n := protowire.ConsumeVarint(tempContainer)
|
||||||
|
if n < 0 {
|
||||||
|
return nil, errInvalidGeodataVarintLength
|
||||||
|
}
|
||||||
|
tempContainer = nil
|
||||||
|
if !isInner {
|
||||||
|
isInner = true
|
||||||
|
geoDataVarintLength = lenVarint
|
||||||
|
advancedN = 1
|
||||||
|
} else {
|
||||||
|
isInner = false
|
||||||
|
codeVarintLength = lenVarint
|
||||||
|
varintLenByteLen = uint64(n)
|
||||||
|
advancedN = codeVarintLength
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
case 5: // data value
|
||||||
|
if strings.EqualFold(string(container), code) {
|
||||||
|
count++
|
||||||
|
offset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength))
|
||||||
|
f.Seek(offset, 1) // back to the start of GeoIP or GeoSite varint
|
||||||
|
advancedN = geoDataVarintLength // the number of bytes to be read in next round
|
||||||
|
} else {
|
||||||
|
count = 1
|
||||||
|
offset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1
|
||||||
|
f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint
|
||||||
|
advancedN = 1 // the next round will be the start of another GeoIPList or GeoSiteList
|
||||||
|
}
|
||||||
|
case 6: // matched GeoIP or GeoSite varint
|
||||||
|
result = container
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decode(filename, code string) ([]byte, error) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
geoBytes, err := emitBytes(f, code)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return geoBytes, nil
|
||||||
|
}
|
40
rule/geodata/memconservative/memc.go
Normal file
40
rule/geodata/memconservative/memc.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package memconservative
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memConservativeLoader struct {
|
||||||
|
geoipcache GeoIPCache
|
||||||
|
geositecache GeoSiteCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
||||||
|
defer runtime.GC()
|
||||||
|
geoip, err := m.geoipcache.Unmarshal(filename, country)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode geodata file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return geoip.Cidr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
||||||
|
defer runtime.GC()
|
||||||
|
geosite, err := m.geositecache.Unmarshal(filename, list)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode geodata file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
return geosite.Domain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemConservativeLoader() geodata.LoaderImplementation {
|
||||||
|
return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
geodata.RegisterGeoDataLoaderImplementationCreator("memconservative", newMemConservativeLoader)
|
||||||
|
}
|
112
rule/geodata/router/condition.go
Normal file
112
rule/geodata/router/condition.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/strmatcher"
|
||||||
|
)
|
||||||
|
|
||||||
|
var matcherTypeMap = map[Domain_Type]strmatcher.Type{
|
||||||
|
Domain_Plain: strmatcher.Substr,
|
||||||
|
Domain_Regex: strmatcher.Regex,
|
||||||
|
Domain_Domain: strmatcher.Domain,
|
||||||
|
Domain_Full: strmatcher.Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
|
||||||
|
matcherType, f := matcherTypeMap[domain.Type]
|
||||||
|
if !f {
|
||||||
|
return nil, fmt.Errorf("unsupported domain type %v", domain.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher, err := matcherType.New(domain.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create domain matcher, base error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return matcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainMatcher struct {
|
||||||
|
matchers strmatcher.IndexMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
|
||||||
|
g := strmatcher.NewMphMatcherGroup()
|
||||||
|
for _, d := range domains {
|
||||||
|
matcherType, f := matcherTypeMap[d.Type]
|
||||||
|
if !f {
|
||||||
|
return nil, fmt.Errorf("unsupported domain type %v", d.Type)
|
||||||
|
}
|
||||||
|
_, err := g.AddPattern(d.Value, matcherType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.Build()
|
||||||
|
return &DomainMatcher{
|
||||||
|
matchers: g,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
||||||
|
g := new(strmatcher.MatcherGroup)
|
||||||
|
for _, d := range domains {
|
||||||
|
m, err := domainToMatcher(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g.Add(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DomainMatcher{
|
||||||
|
matchers: g,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DomainMatcher) ApplyDomain(domain string) bool {
|
||||||
|
return len(m.matchers.Match(strings.ToLower(domain))) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type MultiGeoIPMatcher struct {
|
||||||
|
matchers []*GeoIPMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiGeoIPMatcher(geoips []*GeoIP) (*MultiGeoIPMatcher, error) {
|
||||||
|
var matchers []*GeoIPMatcher
|
||||||
|
for _, geoip := range geoips {
|
||||||
|
matcher, err := globalGeoIPContainer.Add(geoip)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher := &MultiGeoIPMatcher{
|
||||||
|
matchers: matchers,
|
||||||
|
}
|
||||||
|
|
||||||
|
return matcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MultiGeoIPMatcher) ApplyIp(ip net.IP) bool {
|
||||||
|
|
||||||
|
for _, matcher := range m.matchers {
|
||||||
|
if matcher.Match(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) {
|
||||||
|
matcher, err := globalGeoIPContainer.Add(geoip)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return matcher, nil
|
||||||
|
}
|
243
rule/geodata/router/condition_geoip.go
Normal file
243
rule/geodata/router/condition_geoip.go
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CIDRList is an alias of []*CIDR to provide sort.Interface.
|
||||||
|
type CIDRList []*CIDR
|
||||||
|
|
||||||
|
// Len implements sort.Interface.
|
||||||
|
func (l *CIDRList) Len() int {
|
||||||
|
return len(*l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less implements sort.Interface.
|
||||||
|
func (l *CIDRList) Less(i int, j int) bool {
|
||||||
|
ci := (*l)[i]
|
||||||
|
cj := (*l)[j]
|
||||||
|
|
||||||
|
if len(ci.Ip) < len(cj.Ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ci.Ip) > len(cj.Ip) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := 0; k < len(ci.Ip); k++ {
|
||||||
|
if ci.Ip[k] < cj.Ip[k] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ci.Ip[k] > cj.Ip[k] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ci.Prefix < cj.Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap implements sort.Interface.
|
||||||
|
func (l *CIDRList) Swap(i int, j int) {
|
||||||
|
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipv6 struct {
|
||||||
|
a uint64
|
||||||
|
b uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoIPMatcher struct {
|
||||||
|
countryCode string
|
||||||
|
reverseMatch bool
|
||||||
|
ip4 []uint32
|
||||||
|
prefix4 []uint8
|
||||||
|
ip6 []ipv6
|
||||||
|
prefix6 []uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize4(ip uint32, prefix uint8) uint32 {
|
||||||
|
return (ip >> (32 - prefix)) << (32 - prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize6(ip ipv6, prefix uint8) ipv6 {
|
||||||
|
if prefix <= 64 {
|
||||||
|
ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
|
||||||
|
ip.b = 0
|
||||||
|
} else {
|
||||||
|
ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
|
||||||
|
ip4Count := 0
|
||||||
|
ip6Count := 0
|
||||||
|
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
ip := cidr.Ip
|
||||||
|
switch len(ip) {
|
||||||
|
case 4:
|
||||||
|
ip4Count++
|
||||||
|
case 16:
|
||||||
|
ip6Count++
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpect ip length: %d", len(ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cidrList := CIDRList(cidrs)
|
||||||
|
sort.Sort(&cidrList)
|
||||||
|
|
||||||
|
m.ip4 = make([]uint32, 0, ip4Count)
|
||||||
|
m.prefix4 = make([]uint8, 0, ip4Count)
|
||||||
|
m.ip6 = make([]ipv6, 0, ip6Count)
|
||||||
|
m.prefix6 = make([]uint8, 0, ip6Count)
|
||||||
|
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
ip := cidr.Ip
|
||||||
|
prefix := uint8(cidr.Prefix)
|
||||||
|
switch len(ip) {
|
||||||
|
case 4:
|
||||||
|
m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
|
||||||
|
m.prefix4 = append(m.prefix4, prefix)
|
||||||
|
case 16:
|
||||||
|
ip6 := ipv6{
|
||||||
|
a: binary.BigEndian.Uint64(ip[0:8]),
|
||||||
|
b: binary.BigEndian.Uint64(ip[8:16]),
|
||||||
|
}
|
||||||
|
ip6 = normalize6(ip6, prefix)
|
||||||
|
|
||||||
|
m.ip6 = append(m.ip6, ip6)
|
||||||
|
m.prefix6 = append(m.prefix6, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
|
||||||
|
m.reverseMatch = isReverseMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GeoIPMatcher) match4(ip uint32) bool {
|
||||||
|
if len(m.ip4) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip < m.ip4[0] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
size := uint32(len(m.ip4))
|
||||||
|
l := uint32(0)
|
||||||
|
r := size
|
||||||
|
for l < r {
|
||||||
|
x := ((l + r) >> 1)
|
||||||
|
if ip < m.ip4[x] {
|
||||||
|
r = x
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nip := normalize4(ip, m.prefix4[x])
|
||||||
|
if nip == m.ip4[x] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
l = x + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func less6(a ipv6, b ipv6) bool {
|
||||||
|
return a.a < b.a || (a.a == b.a && a.b < b.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GeoIPMatcher) match6(ip ipv6) bool {
|
||||||
|
if len(m.ip6) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if less6(ip, m.ip6[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
size := uint32(len(m.ip6))
|
||||||
|
l := uint32(0)
|
||||||
|
r := size
|
||||||
|
for l < r {
|
||||||
|
x := (l + r) / 2
|
||||||
|
if less6(ip, m.ip6[x]) {
|
||||||
|
r = x
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
l = x + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the given ip is included by the GeoIP.
|
||||||
|
func (m *GeoIPMatcher) Match(ip net.IP) bool {
|
||||||
|
switch len(ip) {
|
||||||
|
case 4:
|
||||||
|
if m.reverseMatch {
|
||||||
|
return !m.match4(binary.BigEndian.Uint32(ip))
|
||||||
|
}
|
||||||
|
return m.match4(binary.BigEndian.Uint32(ip))
|
||||||
|
case 16:
|
||||||
|
if m.reverseMatch {
|
||||||
|
return !m.match6(ipv6{
|
||||||
|
a: binary.BigEndian.Uint64(ip[0:8]),
|
||||||
|
b: binary.BigEndian.Uint64(ip[8:16]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return m.match6(ipv6{
|
||||||
|
a: binary.BigEndian.Uint64(ip[0:8]),
|
||||||
|
b: binary.BigEndian.Uint64(ip[8:16]),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
|
||||||
|
type GeoIPMatcherContainer struct {
|
||||||
|
matchers []*GeoIPMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new GeoIP set into the container.
|
||||||
|
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
|
||||||
|
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
|
||||||
|
if len(geoip.CountryCode) > 0 {
|
||||||
|
for _, m := range c.matchers {
|
||||||
|
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &GeoIPMatcher{
|
||||||
|
countryCode: geoip.CountryCode,
|
||||||
|
reverseMatch: geoip.ReverseMatch,
|
||||||
|
}
|
||||||
|
if err := m.Init(geoip.Cidr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(geoip.CountryCode) > 0 {
|
||||||
|
c.matchers = append(c.matchers, m)
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalGeoIPContainer GeoIPMatcherContainer
|
720
rule/geodata/router/config.pb.go
Normal file
720
rule/geodata/router/config.pb.go
Normal file
|
@ -0,0 +1,720 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.26.0
|
||||||
|
// protoc v3.15.8
|
||||||
|
// source: config.proto
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type of domain value.
|
||||||
|
type Domain_Type int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The value is used as is.
|
||||||
|
Domain_Plain Domain_Type = 0
|
||||||
|
// The value is used as a regular expression.
|
||||||
|
Domain_Regex Domain_Type = 1
|
||||||
|
// The value is a root domain.
|
||||||
|
Domain_Domain Domain_Type = 2
|
||||||
|
// The value is a domain.
|
||||||
|
Domain_Full Domain_Type = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for Domain_Type.
|
||||||
|
var (
|
||||||
|
Domain_Type_name = map[int32]string{
|
||||||
|
0: "Plain",
|
||||||
|
1: "Regex",
|
||||||
|
2: "Domain",
|
||||||
|
3: "Full",
|
||||||
|
}
|
||||||
|
Domain_Type_value = map[string]int32{
|
||||||
|
"Plain": 0,
|
||||||
|
"Regex": 1,
|
||||||
|
"Domain": 2,
|
||||||
|
"Full": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x Domain_Type) Enum() *Domain_Type {
|
||||||
|
p := new(Domain_Type)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Domain_Type) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Domain_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_config_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Domain_Type) Type() protoreflect.EnumType {
|
||||||
|
return &file_config_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Domain_Type) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Domain_Type.Descriptor instead.
|
||||||
|
func (Domain_Type) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain for routing decision.
|
||||||
|
type Domain struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// Domain matching type.
|
||||||
|
Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=clash.rule.geodata.router.Domain_Type" json:"type,omitempty"`
|
||||||
|
// Domain value.
|
||||||
|
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
// Attributes of this domain. May be used for filtering.
|
||||||
|
Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain) Reset() {
|
||||||
|
*x = Domain{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Domain) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Domain) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Domain.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Domain) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain) GetType() Domain_Type {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return Domain_Plain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain) GetValue() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain) GetAttribute() []*Domain_Attribute {
|
||||||
|
if x != nil {
|
||||||
|
return x.Attribute
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP for routing decision, in CIDR form.
|
||||||
|
type CIDR struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// IP address, should be either 4 or 16 bytes.
|
||||||
|
Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||||
|
// Number of leading ones in the network mask.
|
||||||
|
Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CIDR) Reset() {
|
||||||
|
*x = CIDR{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CIDR) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CIDR) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CIDR) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CIDR.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CIDR) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CIDR) GetIp() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Ip
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CIDR) GetPrefix() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Prefix
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoIP struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||||
|
Cidr []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"`
|
||||||
|
ReverseMatch bool `protobuf:"varint,3,opt,name=reverse_match,json=reverseMatch,proto3" json:"reverse_match,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIP) Reset() {
|
||||||
|
*x = GeoIP{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIP) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GeoIP) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GeoIP) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GeoIP) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIP) GetCountryCode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountryCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIP) GetCidr() []*CIDR {
|
||||||
|
if x != nil {
|
||||||
|
return x.Cidr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIP) GetReverseMatch() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.ReverseMatch
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoIPList struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Entry []*GeoIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIPList) Reset() {
|
||||||
|
*x = GeoIPList{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIPList) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GeoIPList) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GeoIPList) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[3]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GeoIPList) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoIPList) GetEntry() []*GeoIP {
|
||||||
|
if x != nil {
|
||||||
|
return x.Entry
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSite struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||||
|
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSite) Reset() {
|
||||||
|
*x = GeoSite{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSite) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GeoSite) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GeoSite) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[4]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GeoSite.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GeoSite) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSite) GetCountryCode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CountryCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSite) GetDomain() []*Domain {
|
||||||
|
if x != nil {
|
||||||
|
return x.Domain
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSiteList struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Entry []*GeoSite `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSiteList) Reset() {
|
||||||
|
*x = GeoSiteList{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSiteList) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GeoSiteList) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GeoSiteList) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[5]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GeoSiteList) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GeoSiteList) GetEntry() []*GeoSite {
|
||||||
|
if x != nil {
|
||||||
|
return x.Entry
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Domain_Attribute struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
// Types that are assignable to TypedValue:
|
||||||
|
// *Domain_Attribute_BoolValue
|
||||||
|
// *Domain_Attribute_IntValue
|
||||||
|
TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) Reset() {
|
||||||
|
*x = Domain_Attribute{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_config_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Domain_Attribute) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_config_proto_msgTypes[6]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Domain_Attribute) Descriptor() ([]byte, []int) {
|
||||||
|
return file_config_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) GetKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Key
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue {
|
||||||
|
if m != nil {
|
||||||
|
return m.TypedValue
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) GetBoolValue() bool {
|
||||||
|
if x, ok := x.GetTypedValue().(*Domain_Attribute_BoolValue); ok {
|
||||||
|
return x.BoolValue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Domain_Attribute) GetIntValue() int64 {
|
||||||
|
if x, ok := x.GetTypedValue().(*Domain_Attribute_IntValue); ok {
|
||||||
|
return x.IntValue
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type isDomain_Attribute_TypedValue interface {
|
||||||
|
isDomain_Attribute_TypedValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Domain_Attribute_BoolValue struct {
|
||||||
|
BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Domain_Attribute_IntValue struct {
|
||||||
|
IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {}
|
||||||
|
|
||||||
|
func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {}
|
||||||
|
|
||||||
|
var File_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19,
|
||||||
|
0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72, 0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61,
|
||||||
|
0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x22, 0xc7, 0x02, 0x0a, 0x06, 0x44, 0x6f,
|
||||||
|
0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x0e, 0x32, 0x26, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72, 0x75, 0x6c, 0x65, 0x2e,
|
||||||
|
0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44,
|
||||||
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||||
|
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x49, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||||
|
0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x61, 0x73,
|
||||||
|
0x68, 0x2e, 0x72, 0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72,
|
||||||
|
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74,
|
||||||
|
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||||
|
0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10,
|
||||||
|
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||||
|
0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
|
||||||
|
0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
|
||||||
|
0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03,
|
||||||
|
0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||||
|
0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
|
||||||
|
0x32, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e,
|
||||||
|
0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, 0x0a,
|
||||||
|
0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
|
||||||
|
0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||||
|
0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70,
|
||||||
|
0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65,
|
||||||
|
0x66, 0x69, 0x78, 0x22, 0x84, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a,
|
||||||
|
0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,
|
||||||
|
0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65,
|
||||||
|
0x12, 0x33, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
|
||||||
|
0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72, 0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64,
|
||||||
|
0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52,
|
||||||
|
0x04, 0x63, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65,
|
||||||
|
0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65,
|
||||||
|
0x76, 0x65, 0x72, 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x43, 0x0a, 0x09, 0x47, 0x65,
|
||||||
|
0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
||||||
|
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72,
|
||||||
|
0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||||
|
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22,
|
||||||
|
0x67, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f,
|
||||||
|
0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x39, 0x0a,
|
||||||
|
0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e,
|
||||||
|
0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72, 0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61,
|
||||||
|
0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x47, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53,
|
||||||
|
0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
||||||
|
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72,
|
||||||
|
0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||||
|
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,
|
||||||
|
0x79, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72,
|
||||||
|
0x75, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||||
|
0x65, 0x72, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x2f, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2f, 0x63, 0x6c, 0x61, 0x73, 0x68,
|
||||||
|
0x2f, 0x72, 0x75, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, 0x6f,
|
||||||
|
0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x19, 0x43, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x52, 0x75, 0x6c,
|
||||||
|
0x65, 0x2e, 0x47, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_config_proto_rawDescOnce sync.Once
|
||||||
|
file_config_proto_rawDescData = file_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
|
var file_config_proto_goTypes = []interface{}{
|
||||||
|
(Domain_Type)(0), // 0: clash.rule.geodata.router.Domain.Type
|
||||||
|
(*Domain)(nil), // 1: clash.rule.geodata.router.Domain
|
||||||
|
(*CIDR)(nil), // 2: clash.rule.geodata.router.CIDR
|
||||||
|
(*GeoIP)(nil), // 3: clash.rule.geodata.router.GeoIP
|
||||||
|
(*GeoIPList)(nil), // 4: clash.rule.geodata.router.GeoIPList
|
||||||
|
(*GeoSite)(nil), // 5: clash.rule.geodata.router.GeoSite
|
||||||
|
(*GeoSiteList)(nil), // 6: clash.rule.geodata.router.GeoSiteList
|
||||||
|
(*Domain_Attribute)(nil), // 7: clash.rule.geodata.router.Domain.Attribute
|
||||||
|
}
|
||||||
|
var file_config_proto_depIdxs = []int32{
|
||||||
|
0, // 0: clash.rule.geodata.router.Domain.type:type_name -> clash.rule.geodata.router.Domain.Type
|
||||||
|
7, // 1: clash.rule.geodata.router.Domain.attribute:type_name -> clash.rule.geodata.router.Domain.Attribute
|
||||||
|
2, // 2: clash.rule.geodata.router.GeoIP.cidr:type_name -> clash.rule.geodata.router.CIDR
|
||||||
|
3, // 3: clash.rule.geodata.router.GeoIPList.entry:type_name -> clash.rule.geodata.router.GeoIP
|
||||||
|
1, // 4: clash.rule.geodata.router.GeoSite.domain:type_name -> clash.rule.geodata.router.Domain
|
||||||
|
5, // 5: clash.rule.geodata.router.GeoSiteList.entry:type_name -> clash.rule.geodata.router.GeoSite
|
||||||
|
6, // [6:6] is the sub-list for method output_type
|
||||||
|
6, // [6:6] is the sub-list for method input_type
|
||||||
|
6, // [6:6] is the sub-list for extension type_name
|
||||||
|
6, // [6:6] is the sub-list for extension extendee
|
||||||
|
0, // [0:6] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_config_proto_init() }
|
||||||
|
func file_config_proto_init() {
|
||||||
|
if File_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Domain); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CIDR); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GeoIP); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GeoIPList); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GeoSite); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GeoSiteList); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Domain_Attribute); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_config_proto_msgTypes[6].OneofWrappers = []interface{}{
|
||||||
|
(*Domain_Attribute_BoolValue)(nil),
|
||||||
|
(*Domain_Attribute_IntValue)(nil),
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_config_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 7,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_config_proto_depIdxs,
|
||||||
|
EnumInfos: file_config_proto_enumTypes,
|
||||||
|
MessageInfos: file_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_config_proto = out.File
|
||||||
|
file_config_proto_rawDesc = nil
|
||||||
|
file_config_proto_goTypes = nil
|
||||||
|
file_config_proto_depIdxs = nil
|
||||||
|
}
|
68
rule/geodata/router/config.proto
Normal file
68
rule/geodata/router/config.proto
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package clash.rule.geodata.router;
|
||||||
|
option csharp_namespace = "Clash.Rule.Geodata.Router";
|
||||||
|
option go_package = "github.com/Dreamacro/clash/rule/geodata/router";
|
||||||
|
option java_package = "com.clash.rule.geodata.router";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
// Domain for routing decision.
|
||||||
|
message Domain {
|
||||||
|
// Type of domain value.
|
||||||
|
enum Type {
|
||||||
|
// The value is used as is.
|
||||||
|
Plain = 0;
|
||||||
|
// The value is used as a regular expression.
|
||||||
|
Regex = 1;
|
||||||
|
// The value is a root domain.
|
||||||
|
Domain = 2;
|
||||||
|
// The value is a domain.
|
||||||
|
Full = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain matching type.
|
||||||
|
Type type = 1;
|
||||||
|
|
||||||
|
// Domain value.
|
||||||
|
string value = 2;
|
||||||
|
|
||||||
|
message Attribute {
|
||||||
|
string key = 1;
|
||||||
|
|
||||||
|
oneof typed_value {
|
||||||
|
bool bool_value = 2;
|
||||||
|
int64 int_value = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes of this domain. May be used for filtering.
|
||||||
|
repeated Attribute attribute = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP for routing decision, in CIDR form.
|
||||||
|
message CIDR {
|
||||||
|
// IP address, should be either 4 or 16 bytes.
|
||||||
|
bytes ip = 1;
|
||||||
|
|
||||||
|
// Number of leading ones in the network mask.
|
||||||
|
uint32 prefix = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GeoIP {
|
||||||
|
string country_code = 1;
|
||||||
|
repeated CIDR cidr = 2;
|
||||||
|
bool reverse_match = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GeoIPList {
|
||||||
|
repeated GeoIP entry = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GeoSite {
|
||||||
|
string country_code = 1;
|
||||||
|
repeated Domain domain = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GeoSiteList {
|
||||||
|
repeated GeoSite entry = 1;
|
||||||
|
}
|
81
rule/geodata/standard/standard.go
Normal file
81
rule/geodata/standard/standard.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package standard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadFile(path string) ([]byte, error) {
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
return ioutil.ReadAll(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadAsset(file string) ([]byte, error) {
|
||||||
|
return ReadFile(C.Path.GetAssetLocation(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadIP(filename, country string) ([]*router.CIDR, error) {
|
||||||
|
geoipBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
var geoipList router.GeoIPList
|
||||||
|
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, geoip := range geoipList.Entry {
|
||||||
|
if strings.EqualFold(geoip.CountryCode, country) {
|
||||||
|
return geoip.Cidr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("country not found in %s%s%s", filename, ": ", country)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSite(filename, list string) ([]*router.Domain, error) {
|
||||||
|
geositeBytes, err := ReadAsset(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error())
|
||||||
|
}
|
||||||
|
var geositeList router.GeoSiteList
|
||||||
|
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, site := range geositeList.Entry {
|
||||||
|
if strings.EqualFold(site.CountryCode, list) {
|
||||||
|
return site.Domain, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("list not found in %s%s%s", filename, ": ", list)
|
||||||
|
}
|
||||||
|
|
||||||
|
type standardLoader struct{}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) {
|
||||||
|
return loadSite(filename, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) {
|
||||||
|
return loadIP(filename, country)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
geodata.RegisterGeoDataLoaderImplementationCreator("standard", func() geodata.LoaderImplementation {
|
||||||
|
return standardLoader{}
|
||||||
|
})
|
||||||
|
}
|
243
rule/geodata/strmatcher/ac_automaton_matcher.go
Normal file
243
rule/geodata/strmatcher/ac_automaton_matcher.go
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
const validCharCount = 53
|
||||||
|
|
||||||
|
type MatchType struct {
|
||||||
|
matchType Type
|
||||||
|
exist bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TrieEdge bool = true
|
||||||
|
FailEdge bool = false
|
||||||
|
)
|
||||||
|
|
||||||
|
type Edge struct {
|
||||||
|
edgeType bool
|
||||||
|
nextNode int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACAutomaton struct {
|
||||||
|
trie [][validCharCount]Edge
|
||||||
|
fail []int
|
||||||
|
exists []MatchType
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNode() [validCharCount]Edge {
|
||||||
|
var s [validCharCount]Edge
|
||||||
|
for i := range s {
|
||||||
|
s[i] = Edge{
|
||||||
|
edgeType: FailEdge,
|
||||||
|
nextNode: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var char2Index = []int{
|
||||||
|
'A': 0,
|
||||||
|
'a': 0,
|
||||||
|
'B': 1,
|
||||||
|
'b': 1,
|
||||||
|
'C': 2,
|
||||||
|
'c': 2,
|
||||||
|
'D': 3,
|
||||||
|
'd': 3,
|
||||||
|
'E': 4,
|
||||||
|
'e': 4,
|
||||||
|
'F': 5,
|
||||||
|
'f': 5,
|
||||||
|
'G': 6,
|
||||||
|
'g': 6,
|
||||||
|
'H': 7,
|
||||||
|
'h': 7,
|
||||||
|
'I': 8,
|
||||||
|
'i': 8,
|
||||||
|
'J': 9,
|
||||||
|
'j': 9,
|
||||||
|
'K': 10,
|
||||||
|
'k': 10,
|
||||||
|
'L': 11,
|
||||||
|
'l': 11,
|
||||||
|
'M': 12,
|
||||||
|
'm': 12,
|
||||||
|
'N': 13,
|
||||||
|
'n': 13,
|
||||||
|
'O': 14,
|
||||||
|
'o': 14,
|
||||||
|
'P': 15,
|
||||||
|
'p': 15,
|
||||||
|
'Q': 16,
|
||||||
|
'q': 16,
|
||||||
|
'R': 17,
|
||||||
|
'r': 17,
|
||||||
|
'S': 18,
|
||||||
|
's': 18,
|
||||||
|
'T': 19,
|
||||||
|
't': 19,
|
||||||
|
'U': 20,
|
||||||
|
'u': 20,
|
||||||
|
'V': 21,
|
||||||
|
'v': 21,
|
||||||
|
'W': 22,
|
||||||
|
'w': 22,
|
||||||
|
'X': 23,
|
||||||
|
'x': 23,
|
||||||
|
'Y': 24,
|
||||||
|
'y': 24,
|
||||||
|
'Z': 25,
|
||||||
|
'z': 25,
|
||||||
|
'!': 26,
|
||||||
|
'$': 27,
|
||||||
|
'&': 28,
|
||||||
|
'\'': 29,
|
||||||
|
'(': 30,
|
||||||
|
')': 31,
|
||||||
|
'*': 32,
|
||||||
|
'+': 33,
|
||||||
|
',': 34,
|
||||||
|
';': 35,
|
||||||
|
'=': 36,
|
||||||
|
':': 37,
|
||||||
|
'%': 38,
|
||||||
|
'-': 39,
|
||||||
|
'.': 40,
|
||||||
|
'_': 41,
|
||||||
|
'~': 42,
|
||||||
|
'0': 43,
|
||||||
|
'1': 44,
|
||||||
|
'2': 45,
|
||||||
|
'3': 46,
|
||||||
|
'4': 47,
|
||||||
|
'5': 48,
|
||||||
|
'6': 49,
|
||||||
|
'7': 50,
|
||||||
|
'8': 51,
|
||||||
|
'9': 52,
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewACAutomaton() *ACAutomaton {
|
||||||
|
ac := new(ACAutomaton)
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Add(domain string, t Type) {
|
||||||
|
node := 0
|
||||||
|
for i := len(domain) - 1; i >= 0; i-- {
|
||||||
|
idx := char2Index[domain[i]]
|
||||||
|
if ac.trie[node][idx].nextNode == 0 {
|
||||||
|
ac.count++
|
||||||
|
if len(ac.trie) < ac.count+1 {
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ac.trie[node][idx] = Edge{
|
||||||
|
edgeType: TrieEdge,
|
||||||
|
nextNode: ac.count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
}
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: t,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case Domain:
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
idx := char2Index['.']
|
||||||
|
if ac.trie[node][idx].nextNode == 0 {
|
||||||
|
ac.count++
|
||||||
|
if len(ac.trie) < ac.count+1 {
|
||||||
|
ac.trie = append(ac.trie, newNode())
|
||||||
|
ac.fail = append(ac.fail, 0)
|
||||||
|
ac.exists = append(ac.exists, MatchType{
|
||||||
|
matchType: Full,
|
||||||
|
exist: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ac.trie[node][idx] = Edge{
|
||||||
|
edgeType: TrieEdge,
|
||||||
|
nextNode: ac.count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
ac.exists[node] = MatchType{
|
||||||
|
matchType: t,
|
||||||
|
exist: true,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Build() {
|
||||||
|
queue := list.New()
|
||||||
|
for i := 0; i < validCharCount; i++ {
|
||||||
|
if ac.trie[0][i].nextNode != 0 {
|
||||||
|
queue.PushBack(ac.trie[0][i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
front := queue.Front()
|
||||||
|
if front == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
node := front.Value.(Edge).nextNode
|
||||||
|
queue.Remove(front)
|
||||||
|
for i := 0; i < validCharCount; i++ {
|
||||||
|
if ac.trie[node][i].nextNode != 0 {
|
||||||
|
ac.fail[ac.trie[node][i].nextNode] = ac.trie[ac.fail[node]][i].nextNode
|
||||||
|
queue.PushBack(ac.trie[node][i])
|
||||||
|
} else {
|
||||||
|
ac.trie[node][i] = Edge{
|
||||||
|
edgeType: FailEdge,
|
||||||
|
nextNode: ac.trie[ac.fail[node]][i].nextNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *ACAutomaton) Match(s string) bool {
|
||||||
|
node := 0
|
||||||
|
fullMatch := true
|
||||||
|
// 1. the match string is all through trie edge. FULL MATCH or DOMAIN
|
||||||
|
// 2. the match string is through a fail edge. NOT FULL MATCH
|
||||||
|
// 2.1 Through a fail edge, but there exists a valid node. SUBSTR
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
idx := char2Index[s[i]]
|
||||||
|
fullMatch = fullMatch && ac.trie[node][idx].edgeType
|
||||||
|
node = ac.trie[node][idx].nextNode
|
||||||
|
switch ac.exists[node].matchType {
|
||||||
|
case Substr:
|
||||||
|
return true
|
||||||
|
case Domain:
|
||||||
|
if fullMatch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
//default:
|
||||||
|
// //break // ineffective break statement, code "break" can not pass staticcheck check. don't sure that mean, so just block it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullMatch && ac.exists[node].exist
|
||||||
|
}
|
98
rule/geodata/strmatcher/domain_matcher.go
Normal file
98
rule/geodata/strmatcher/domain_matcher.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func breakDomain(domain string) []string {
|
||||||
|
return strings.Split(domain, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
values []uint32
|
||||||
|
sub map[string]*node
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainMatcherGroup is a IndexMatcher for a large set of Domain matchers.
|
||||||
|
// Visible for testing only.
|
||||||
|
type DomainMatcherGroup struct {
|
||||||
|
root *node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *DomainMatcherGroup) Add(domain string, value uint32) {
|
||||||
|
if g.root == nil {
|
||||||
|
g.root = new(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
current := g.root
|
||||||
|
parts := breakDomain(domain)
|
||||||
|
for i := len(parts) - 1; i >= 0; i-- {
|
||||||
|
part := parts[i]
|
||||||
|
if current.sub == nil {
|
||||||
|
current.sub = make(map[string]*node)
|
||||||
|
}
|
||||||
|
next := current.sub[part]
|
||||||
|
if next == nil {
|
||||||
|
next = new(node)
|
||||||
|
current.sub[part] = next
|
||||||
|
}
|
||||||
|
current = next
|
||||||
|
}
|
||||||
|
|
||||||
|
current.values = append(current.values, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *DomainMatcherGroup) addMatcher(m domainMatcher, value uint32) {
|
||||||
|
g.Add(string(m), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *DomainMatcherGroup) Match(domain string) []uint32 {
|
||||||
|
if domain == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
current := g.root
|
||||||
|
if current == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPart := func(idx int) int {
|
||||||
|
for i := idx - 1; i >= 0; i-- {
|
||||||
|
if domain[i] == '.' {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := [][]uint32{}
|
||||||
|
idx := len(domain)
|
||||||
|
for {
|
||||||
|
if idx == -1 || current.sub == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
nidx := nextPart(idx)
|
||||||
|
part := domain[nidx+1 : idx]
|
||||||
|
next := current.sub[part]
|
||||||
|
if next == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = next
|
||||||
|
idx = nidx
|
||||||
|
if len(current.values) > 0 {
|
||||||
|
matches = append(matches, current.values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch len(matches) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return matches[0]
|
||||||
|
default:
|
||||||
|
result := []uint32{}
|
||||||
|
for idx := range matches {
|
||||||
|
// Insert reversely, the subdomain that matches further ranks higher
|
||||||
|
result = append(result, matches[len(matches)-1-idx]...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
25
rule/geodata/strmatcher/full_matcher.go
Normal file
25
rule/geodata/strmatcher/full_matcher.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
type FullMatcherGroup struct {
|
||||||
|
matchers map[string][]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *FullMatcherGroup) Add(domain string, value uint32) {
|
||||||
|
if g.matchers == nil {
|
||||||
|
g.matchers = make(map[string][]uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.matchers[domain] = append(g.matchers[domain], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *FullMatcherGroup) addMatcher(m fullMatcher, value uint32) {
|
||||||
|
g.Add(string(m), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *FullMatcherGroup) Match(str string) []uint32 {
|
||||||
|
if g.matchers == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.matchers[str]
|
||||||
|
}
|
52
rule/geodata/strmatcher/matchers.go
Normal file
52
rule/geodata/strmatcher/matchers.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fullMatcher string
|
||||||
|
|
||||||
|
func (m fullMatcher) Match(s string) bool {
|
||||||
|
return string(m) == s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m fullMatcher) String() string {
|
||||||
|
return "full:" + string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
type substrMatcher string
|
||||||
|
|
||||||
|
func (m substrMatcher) Match(s string) bool {
|
||||||
|
return strings.Contains(s, string(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m substrMatcher) String() string {
|
||||||
|
return "keyword:" + string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainMatcher string
|
||||||
|
|
||||||
|
func (m domainMatcher) Match(s string) bool {
|
||||||
|
pattern := string(m)
|
||||||
|
if !strings.HasSuffix(s, pattern) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m domainMatcher) String() string {
|
||||||
|
return "domain:" + string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
type regexMatcher struct {
|
||||||
|
pattern *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *regexMatcher) Match(s string) bool {
|
||||||
|
return m.pattern.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *regexMatcher) String() string {
|
||||||
|
return "regexp:" + m.pattern.String()
|
||||||
|
}
|
304
rule/geodata/strmatcher/mph_matcher.go
Normal file
304
rule/geodata/strmatcher/mph_matcher.go
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/bits"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||||
|
const PrimeRK = 16777619
|
||||||
|
|
||||||
|
// calculate the rolling murmurHash of given string
|
||||||
|
func RollingHash(s string) uint32 {
|
||||||
|
h := uint32(0)
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
h = h*PrimeRK + uint32(s[i])
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MphMatcherGroup is divided into three parts:
|
||||||
|
// 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table;
|
||||||
|
// 2. `substr` patterns are matched by ac automaton;
|
||||||
|
// 3. `regex` patterns are matched with the regex library.
|
||||||
|
type MphMatcherGroup struct {
|
||||||
|
ac *ACAutomaton
|
||||||
|
otherMatchers []matcherEntry
|
||||||
|
rules []string
|
||||||
|
level0 []uint32
|
||||||
|
level0Mask int
|
||||||
|
level1 []uint32
|
||||||
|
level1Mask int
|
||||||
|
count uint32
|
||||||
|
ruleMap *map[string]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *MphMatcherGroup) AddFullOrDomainPattern(pattern string, t Type) {
|
||||||
|
h := RollingHash(pattern)
|
||||||
|
switch t {
|
||||||
|
case Domain:
|
||||||
|
(*g.ruleMap)["."+pattern] = h*PrimeRK + uint32('.')
|
||||||
|
fallthrough
|
||||||
|
case Full:
|
||||||
|
(*g.ruleMap)[pattern] = h
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMphMatcherGroup() *MphMatcherGroup {
|
||||||
|
return &MphMatcherGroup{
|
||||||
|
ac: nil,
|
||||||
|
otherMatchers: nil,
|
||||||
|
rules: nil,
|
||||||
|
level0: nil,
|
||||||
|
level0Mask: 0,
|
||||||
|
level1: nil,
|
||||||
|
level1Mask: 0,
|
||||||
|
count: 1,
|
||||||
|
ruleMap: &map[string]uint32{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPattern adds a pattern to MphMatcherGroup
|
||||||
|
func (g *MphMatcherGroup) AddPattern(pattern string, t Type) (uint32, error) {
|
||||||
|
switch t {
|
||||||
|
case Substr:
|
||||||
|
if g.ac == nil {
|
||||||
|
g.ac = NewACAutomaton()
|
||||||
|
}
|
||||||
|
g.ac.Add(pattern, t)
|
||||||
|
case Full, Domain:
|
||||||
|
pattern = strings.ToLower(pattern)
|
||||||
|
g.AddFullOrDomainPattern(pattern, t)
|
||||||
|
case Regex:
|
||||||
|
r, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
g.otherMatchers = append(g.otherMatchers, matcherEntry{
|
||||||
|
m: ®exMatcher{pattern: r},
|
||||||
|
id: g.count,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic("Unknown type")
|
||||||
|
}
|
||||||
|
return g.count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build builds a minimal perfect hash table and ac automaton from insert rules
|
||||||
|
func (g *MphMatcherGroup) Build() {
|
||||||
|
if g.ac != nil {
|
||||||
|
g.ac.Build()
|
||||||
|
}
|
||||||
|
keyLen := len(*g.ruleMap)
|
||||||
|
if keyLen == 0 {
|
||||||
|
keyLen = 1
|
||||||
|
(*g.ruleMap)["empty___"] = RollingHash("empty___")
|
||||||
|
}
|
||||||
|
g.level0 = make([]uint32, nextPow2(keyLen/4))
|
||||||
|
g.level0Mask = len(g.level0) - 1
|
||||||
|
g.level1 = make([]uint32, nextPow2(keyLen))
|
||||||
|
g.level1Mask = len(g.level1) - 1
|
||||||
|
sparseBuckets := make([][]int, len(g.level0))
|
||||||
|
var ruleIdx int
|
||||||
|
for rule, hash := range *g.ruleMap {
|
||||||
|
n := int(hash) & g.level0Mask
|
||||||
|
g.rules = append(g.rules, rule)
|
||||||
|
sparseBuckets[n] = append(sparseBuckets[n], ruleIdx)
|
||||||
|
ruleIdx++
|
||||||
|
}
|
||||||
|
g.ruleMap = nil
|
||||||
|
var buckets []indexBucket
|
||||||
|
for n, vals := range sparseBuckets {
|
||||||
|
if len(vals) > 0 {
|
||||||
|
buckets = append(buckets, indexBucket{n, vals})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(bySize(buckets))
|
||||||
|
|
||||||
|
occ := make([]bool, len(g.level1))
|
||||||
|
var tmpOcc []int
|
||||||
|
for _, bucket := range buckets {
|
||||||
|
seed := uint32(0)
|
||||||
|
for {
|
||||||
|
findSeed := true
|
||||||
|
tmpOcc = tmpOcc[:0]
|
||||||
|
for _, i := range bucket.vals {
|
||||||
|
n := int(strhashFallback(unsafe.Pointer(&g.rules[i]), uintptr(seed))) & g.level1Mask
|
||||||
|
if occ[n] {
|
||||||
|
for _, n := range tmpOcc {
|
||||||
|
occ[n] = false
|
||||||
|
}
|
||||||
|
seed++
|
||||||
|
findSeed = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
occ[n] = true
|
||||||
|
tmpOcc = append(tmpOcc, n)
|
||||||
|
g.level1[n] = uint32(i)
|
||||||
|
}
|
||||||
|
if findSeed {
|
||||||
|
g.level0[bucket.n] = seed
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPow2(v int) int {
|
||||||
|
if v <= 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
const MaxUInt = ^uint(0)
|
||||||
|
n := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1
|
||||||
|
return int(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup searches for s in t and returns its index and whether it was found.
|
||||||
|
func (g *MphMatcherGroup) Lookup(h uint32, s string) bool {
|
||||||
|
i0 := int(h) & g.level0Mask
|
||||||
|
seed := g.level0[i0]
|
||||||
|
i1 := int(strhashFallback(unsafe.Pointer(&s), uintptr(seed))) & g.level1Mask
|
||||||
|
n := g.level1[i1]
|
||||||
|
return s == g.rules[int(n)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements IndexMatcher.Match.
|
||||||
|
func (g *MphMatcherGroup) Match(pattern string) []uint32 {
|
||||||
|
result := []uint32{}
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := len(pattern) - 1; i >= 0; i-- {
|
||||||
|
hash = hash*PrimeRK + uint32(pattern[i])
|
||||||
|
if pattern[i] == '.' {
|
||||||
|
if g.Lookup(hash, pattern[i:]) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if g.Lookup(hash, pattern) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if g.ac != nil && g.ac.Match(pattern) {
|
||||||
|
result = append(result, 1)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for _, e := range g.otherMatchers {
|
||||||
|
if e.m.Match(pattern) {
|
||||||
|
result = append(result, e.id)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexBucket struct {
|
||||||
|
n int
|
||||||
|
vals []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type bySize []indexBucket
|
||||||
|
|
||||||
|
func (s bySize) Len() int { return len(s) }
|
||||||
|
func (s bySize) Less(i, j int) bool { return len(s[i].vals) > len(s[j].vals) }
|
||||||
|
func (s bySize) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
|
type stringStruct struct {
|
||||||
|
str unsafe.Pointer
|
||||||
|
len int
|
||||||
|
}
|
||||||
|
|
||||||
|
func strhashFallback(a unsafe.Pointer, h uintptr) uintptr {
|
||||||
|
x := (*stringStruct)(a)
|
||||||
|
return memhashFallback(x.str, h, uintptr(x.len))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Constants for multiplication: four random odd 64-bit numbers.
|
||||||
|
m1 = 16877499708836156737
|
||||||
|
m2 = 2820277070424839065
|
||||||
|
m3 = 9497967016996688599
|
||||||
|
m4 = 15839092249703872147
|
||||||
|
)
|
||||||
|
|
||||||
|
var hashkey = [4]uintptr{1, 1, 1, 1}
|
||||||
|
|
||||||
|
func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||||
|
h := uint64(seed + s*hashkey[0])
|
||||||
|
tail:
|
||||||
|
switch {
|
||||||
|
case s == 0:
|
||||||
|
case s < 4:
|
||||||
|
h ^= uint64(*(*byte)(p))
|
||||||
|
h ^= uint64(*(*byte)(add(p, s>>1))) << 8
|
||||||
|
h ^= uint64(*(*byte)(add(p, s-1))) << 16
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 8:
|
||||||
|
h ^= uint64(readUnaligned32(p))
|
||||||
|
h ^= uint64(readUnaligned32(add(p, s-4))) << 32
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 16:
|
||||||
|
h ^= readUnaligned64(p)
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
case s <= 32:
|
||||||
|
h ^= readUnaligned64(p)
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, 8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-16))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
h ^= readUnaligned64(add(p, s-8))
|
||||||
|
h = rotl31(h*m1) * m2
|
||||||
|
default:
|
||||||
|
v1 := h
|
||||||
|
v2 := uint64(seed * hashkey[1])
|
||||||
|
v3 := uint64(seed * hashkey[2])
|
||||||
|
v4 := uint64(seed * hashkey[3])
|
||||||
|
for s >= 32 {
|
||||||
|
v1 ^= readUnaligned64(p)
|
||||||
|
v1 = rotl31(v1*m1) * m2
|
||||||
|
p = add(p, 8)
|
||||||
|
v2 ^= readUnaligned64(p)
|
||||||
|
v2 = rotl31(v2*m2) * m3
|
||||||
|
p = add(p, 8)
|
||||||
|
v3 ^= readUnaligned64(p)
|
||||||
|
v3 = rotl31(v3*m3) * m4
|
||||||
|
p = add(p, 8)
|
||||||
|
v4 ^= readUnaligned64(p)
|
||||||
|
v4 = rotl31(v4*m4) * m1
|
||||||
|
p = add(p, 8)
|
||||||
|
s -= 32
|
||||||
|
}
|
||||||
|
h = v1 ^ v2 ^ v3 ^ v4
|
||||||
|
goto tail
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= h >> 29
|
||||||
|
h *= m3
|
||||||
|
h ^= h >> 32
|
||||||
|
return uintptr(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUnaligned32(p unsafe.Pointer) uint32 {
|
||||||
|
q := (*[4]byte)(p)
|
||||||
|
return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotl31(x uint64) uint64 {
|
||||||
|
return (x << 31) | (x >> (64 - 31))
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUnaligned64(p unsafe.Pointer) uint64 {
|
||||||
|
q := (*[8]byte)(p)
|
||||||
|
return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56
|
||||||
|
}
|
107
rule/geodata/strmatcher/strmatcher.go
Normal file
107
rule/geodata/strmatcher/strmatcher.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package strmatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matcher is the interface to determine a string matches a pattern.
|
||||||
|
type Matcher interface {
|
||||||
|
// Match returns true if the given string matches a predefined pattern.
|
||||||
|
Match(string) bool
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is the type of the matcher.
|
||||||
|
type Type byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Full is the type of matcher that the input string must exactly equal to the pattern.
|
||||||
|
Full Type = iota
|
||||||
|
// Substr is the type of matcher that the input string must contain the pattern as a sub-string.
|
||||||
|
Substr
|
||||||
|
// Domain is the type of matcher that the input string must be a sub-domain or itself of the pattern.
|
||||||
|
Domain
|
||||||
|
// Regex is the type of matcher that the input string must matches the regular-expression pattern.
|
||||||
|
Regex
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates a new Matcher based on the given pattern.
|
||||||
|
func (t Type) New(pattern string) (Matcher, error) {
|
||||||
|
// 1. regex matching is case-sensitive
|
||||||
|
switch t {
|
||||||
|
case Full:
|
||||||
|
return fullMatcher(pattern), nil
|
||||||
|
case Substr:
|
||||||
|
return substrMatcher(pattern), nil
|
||||||
|
case Domain:
|
||||||
|
return domainMatcher(pattern), nil
|
||||||
|
case Regex:
|
||||||
|
r, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ®exMatcher{
|
||||||
|
pattern: r,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
panic("Unknown type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexMatcher is the interface for matching with a group of matchers.
|
||||||
|
type IndexMatcher interface {
|
||||||
|
// Match returns the index of a matcher that matches the input. It returns empty array if no such matcher exists.
|
||||||
|
Match(input string) []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type matcherEntry struct {
|
||||||
|
m Matcher
|
||||||
|
id uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatcherGroup is an implementation of IndexMatcher.
|
||||||
|
// Empty initialization works.
|
||||||
|
type MatcherGroup struct {
|
||||||
|
count uint32
|
||||||
|
fullMatcher FullMatcherGroup
|
||||||
|
domainMatcher DomainMatcherGroup
|
||||||
|
otherMatchers []matcherEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new Matcher into the MatcherGroup, and returns its index. The index will never be 0.
|
||||||
|
func (g *MatcherGroup) Add(m Matcher) uint32 {
|
||||||
|
g.count++
|
||||||
|
c := g.count
|
||||||
|
|
||||||
|
switch tm := m.(type) {
|
||||||
|
case fullMatcher:
|
||||||
|
g.fullMatcher.addMatcher(tm, c)
|
||||||
|
case domainMatcher:
|
||||||
|
g.domainMatcher.addMatcher(tm, c)
|
||||||
|
default:
|
||||||
|
g.otherMatchers = append(g.otherMatchers, matcherEntry{
|
||||||
|
m: m,
|
||||||
|
id: c,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements IndexMatcher.Match.
|
||||||
|
func (g *MatcherGroup) Match(pattern string) []uint32 {
|
||||||
|
result := []uint32{}
|
||||||
|
result = append(result, g.fullMatcher.Match(pattern)...)
|
||||||
|
result = append(result, g.domainMatcher.Match(pattern)...)
|
||||||
|
for _, e := range g.otherMatchers {
|
||||||
|
if e.m.Match(pattern) {
|
||||||
|
result = append(result, e.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the number of matchers in the MatcherGroup.
|
||||||
|
func (g *MatcherGroup) Size() uint32 {
|
||||||
|
return g.count
|
||||||
|
}
|
|
@ -1,14 +1,22 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Dreamacro/clash/component/mmdb"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
_ "github.com/Dreamacro/clash/rule/geodata/standard"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GEOIP struct {
|
type GEOIP struct {
|
||||||
country string
|
country string
|
||||||
adapter string
|
adapter string
|
||||||
noResolveIP bool
|
noResolveIP bool
|
||||||
|
network C.NetWork
|
||||||
|
geoIPMatcher *router.GeoIPMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GEOIP) RuleType() C.RuleType {
|
func (g *GEOIP) RuleType() C.RuleType {
|
||||||
|
@ -20,8 +28,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool {
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
record, _ := mmdb.Instance().Country(ip)
|
return g.geoIPMatcher.Match(ip)
|
||||||
return record.Country.IsoCode == g.country
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GEOIP) Adapter() string {
|
func (g *GEOIP) Adapter() string {
|
||||||
|
@ -36,12 +43,44 @@ func (g *GEOIP) ShouldResolveIP() bool {
|
||||||
return !g.noResolveIP
|
return !g.noResolveIP
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGEOIP(country string, adapter string, noResolveIP bool) *GEOIP {
|
func (g *GEOIP) NetWork() C.NetWork {
|
||||||
geoip := &GEOIP{
|
return g.network
|
||||||
country: country,
|
}
|
||||||
adapter: adapter,
|
|
||||||
noResolveIP: noResolveIP,
|
func NewGEOIP(country string, adapter string, noResolveIP bool, network C.NetWork) (*GEOIP, error) {
|
||||||
|
geoLoaderName := "standard"
|
||||||
|
//geoLoaderName := "memconservative"
|
||||||
|
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return geoip
|
records, err := geoLoader.LoadGeoIP(strings.ReplaceAll(country, "!", ""))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
geoIP := &router.GeoIP{
|
||||||
|
CountryCode: country,
|
||||||
|
Cidr: records,
|
||||||
|
ReverseMatch: strings.Contains(country, "!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
geoIPMatcher, err := router.NewGeoIPMatcher(geoIP)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, len(records))
|
||||||
|
|
||||||
|
geoip := &GEOIP{
|
||||||
|
country: country,
|
||||||
|
adapter: adapter,
|
||||||
|
noResolveIP: noResolveIP,
|
||||||
|
network: network,
|
||||||
|
geoIPMatcher: geoIPMatcher,
|
||||||
|
}
|
||||||
|
|
||||||
|
return geoip, nil
|
||||||
}
|
}
|
||||||
|
|
82
rule/geosite.go
Normal file
82
rule/geosite.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata"
|
||||||
|
//_ "github.com/Dreamacro/clash/rule/geodata/memconservative"
|
||||||
|
"github.com/Dreamacro/clash/rule/geodata/router"
|
||||||
|
_ "github.com/Dreamacro/clash/rule/geodata/standard"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GEOSITE struct {
|
||||||
|
country string
|
||||||
|
adapter string
|
||||||
|
network C.NetWork
|
||||||
|
matcher *router.DomainMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) RuleType() C.RuleType {
|
||||||
|
return C.GEOSITE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) Match(metadata *C.Metadata) bool {
|
||||||
|
if metadata.AddrType != C.AtypDomainName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := metadata.Host
|
||||||
|
return gs.matcher.ApplyDomain(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) Adapter() string {
|
||||||
|
return gs.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) Payload() string {
|
||||||
|
return gs.country
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) ShouldResolveIP() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GEOSITE) NetWork() C.NetWork {
|
||||||
|
return gs.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGEOSITE(country string, adapter string, network C.NetWork) (*GEOSITE, error) {
|
||||||
|
geoLoaderName := "standard"
|
||||||
|
//geoLoaderName := "memconservative"
|
||||||
|
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoSite] %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
domains, err := geoLoader.LoadGeoSite(country)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoSite] %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
//linear: linear algorithm
|
||||||
|
//matcher, err := router.NewDomainMatcher(domains)
|
||||||
|
|
||||||
|
//mph:minimal perfect hash algorithm
|
||||||
|
matcher, err := router.NewMphMatcherGroup(domains)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("[GeoSite] %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, len(domains))
|
||||||
|
|
||||||
|
geoSite := &GEOSITE{
|
||||||
|
country: country,
|
||||||
|
adapter: adapter,
|
||||||
|
network: network,
|
||||||
|
matcher: matcher,
|
||||||
|
}
|
||||||
|
|
||||||
|
return geoSite, nil
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
|
||||||
type IPCIDR struct {
|
type IPCIDR struct {
|
||||||
ipnet *net.IPNet
|
ipnet *net.IPNet
|
||||||
adapter string
|
adapter string
|
||||||
|
network C.NetWork
|
||||||
isSourceIP bool
|
isSourceIP bool
|
||||||
noResolveIP bool
|
noResolveIP bool
|
||||||
}
|
}
|
||||||
|
@ -54,7 +55,11 @@ func (i *IPCIDR) ShouldResolveIP() bool {
|
||||||
return !i.noResolveIP
|
return !i.noResolveIP
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
func (i *IPCIDR) NetWork() C.NetWork {
|
||||||
|
return i.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIPCIDR(s string, adapter string, network C.NetWork, opts ...IPCIDROption) (*IPCIDR, error) {
|
||||||
_, ipnet, err := net.ParseCIDR(s)
|
_, ipnet, err := net.ParseCIDR(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errPayload
|
return nil, errPayload
|
||||||
|
@ -63,6 +68,7 @@ func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error)
|
||||||
ipcidr := &IPCIDR{
|
ipcidr := &IPCIDR{
|
||||||
ipnet: ipnet,
|
ipnet: ipnet,
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
network: network,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
|
|
@ -10,29 +10,32 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
|
||||||
var (
|
var (
|
||||||
parseErr error
|
parseErr error
|
||||||
parsed C.Rule
|
parsed C.Rule
|
||||||
|
network = findNetwork(params)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch tp {
|
switch tp {
|
||||||
case "DOMAIN":
|
case "DOMAIN":
|
||||||
parsed = NewDomain(payload, target)
|
parsed = NewDomain(payload, target, network)
|
||||||
case "DOMAIN-SUFFIX":
|
case "DOMAIN-SUFFIX":
|
||||||
parsed = NewDomainSuffix(payload, target)
|
parsed = NewDomainSuffix(payload, target, network)
|
||||||
case "DOMAIN-KEYWORD":
|
case "DOMAIN-KEYWORD":
|
||||||
parsed = NewDomainKeyword(payload, target)
|
parsed = NewDomainKeyword(payload, target, network)
|
||||||
|
case "GEOSITE":
|
||||||
|
parsed, parseErr = NewGEOSITE(payload, target, network)
|
||||||
case "GEOIP":
|
case "GEOIP":
|
||||||
noResolve := HasNoResolve(params)
|
noResolve := HasNoResolve(params)
|
||||||
parsed = NewGEOIP(payload, target, noResolve)
|
parsed, parseErr = NewGEOIP(payload, target, noResolve, network)
|
||||||
case "IP-CIDR", "IP-CIDR6":
|
case "IP-CIDR", "IP-CIDR6":
|
||||||
noResolve := HasNoResolve(params)
|
noResolve := HasNoResolve(params)
|
||||||
parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRNoResolve(noResolve))
|
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRNoResolve(noResolve))
|
||||||
case "SRC-IP-CIDR":
|
case "SRC-IP-CIDR":
|
||||||
parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
|
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
|
||||||
case "SRC-PORT":
|
case "SRC-PORT":
|
||||||
parsed, parseErr = NewPort(payload, target, true)
|
parsed, parseErr = NewPort(payload, target, true, network)
|
||||||
case "DST-PORT":
|
case "DST-PORT":
|
||||||
parsed, parseErr = NewPort(payload, target, false)
|
parsed, parseErr = NewPort(payload, target, false, network)
|
||||||
case "PROCESS-NAME":
|
case "PROCESS-NAME":
|
||||||
parsed, parseErr = NewProcess(payload, target)
|
parsed, parseErr = NewProcess(payload, target, network)
|
||||||
case "MATCH":
|
case "MATCH":
|
||||||
parsed = NewMatch(target)
|
parsed = NewMatch(target)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -10,6 +10,7 @@ type Port struct {
|
||||||
adapter string
|
adapter string
|
||||||
port string
|
port string
|
||||||
isSource bool
|
isSource bool
|
||||||
|
network C.NetWork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Port) RuleType() C.RuleType {
|
func (p *Port) RuleType() C.RuleType {
|
||||||
|
@ -38,7 +39,11 @@ func (p *Port) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
func (p *Port) NetWork() C.NetWork {
|
||||||
|
return p.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Port, error) {
|
||||||
_, err := strconv.Atoi(port)
|
_, err := strconv.Atoi(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errPayload
|
return nil, errPayload
|
||||||
|
@ -47,5 +52,6 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
port: port,
|
port: port,
|
||||||
isSource: isSource,
|
isSource: isSource,
|
||||||
|
network: network,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
||||||
type Process struct {
|
type Process struct {
|
||||||
adapter string
|
adapter string
|
||||||
process string
|
process string
|
||||||
|
network C.NetWork
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *Process) RuleType() C.RuleType {
|
func (ps *Process) RuleType() C.RuleType {
|
||||||
|
@ -23,6 +24,17 @@ func (ps *Process) RuleType() C.RuleType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *Process) Match(metadata *C.Metadata) bool {
|
func (ps *Process) Match(metadata *C.Metadata) bool {
|
||||||
|
|
||||||
|
if metadata.Process != "" {
|
||||||
|
//log.Debugln("Use cache process: %s", metadata.Process)
|
||||||
|
return strings.EqualFold(metadata.Process, ps.process)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore match in proxy type "tproxy"
|
||||||
|
if metadata.Type == C.TPROXY {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
||||||
cached, hit := processCache.Get(key)
|
cached, hit := processCache.Get(key)
|
||||||
if !hit {
|
if !hit {
|
||||||
|
@ -42,7 +54,9 @@ func (ps *Process) Match(metadata *C.Metadata) bool {
|
||||||
cached = name
|
cached = name
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.EqualFold(cached.(string), ps.process)
|
metadata.Process = cached.(string)
|
||||||
|
|
||||||
|
return strings.EqualFold(metadata.Process, ps.process)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *Process) Adapter() string {
|
func (ps *Process) Adapter() string {
|
||||||
|
@ -57,9 +71,14 @@ func (ps *Process) ShouldResolveIP() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (*Process, error) {
|
func (ps *Process) NetWork() C.NetWork {
|
||||||
|
return ps.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProcess(process string, adapter string, network C.NetWork) (*Process, error) {
|
||||||
return &Process{
|
return &Process{
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
process: process,
|
process: process,
|
||||||
|
network: network,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue