merge dev

This commit is contained in:
ICKelin
2024-04-29 12:46:40 +08:00
118 changed files with 148432 additions and 2101 deletions

View File

@@ -15,4 +15,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: ./build_image.sh
run: ./docker_build.sh

View File

@@ -22,5 +22,5 @@ jobs:
go-version: 1.19
- name: Build
run: ./build_exec.sh
run: ./build.sh

4
.gitignore vendored
View File

@@ -12,4 +12,6 @@ research
*.tar.gz
docker-build/gtun/gtun*
docker-build/gtund/gtund
docker-build/gtund/gtund
release
images

View File

@@ -1,14 +0,0 @@
## gtun
<a href="">
<img src="https://img.shields.io/badge/-Go-000?&logo=go">
</a>
<a href="https://goreportcard.com/report/github.com/ICKelin/gtun" rel="nofollow">
<img src="https://goreportcard.com/badge/github.com/ICKelin/gtun" alt="go report">
</a>
<a href="https://travis-ci.org/ICKelin/gtun" rel="nofollow">
<img src="https://travis-ci.org/ICKelin/gtun.svg?branch=master" alt="Build Status">
</a>
<a href="https://github.com/ICKelin/gtun/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>

171
README.md
View File

@@ -13,27 +13,18 @@
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
gtun是一款开源的ip代理加速软件通过`tproxy`技术实现流量劫持,`quic``kcp`等协议优化广域网传输gtun提供一个基础通道所有加入`ipset`的ip,出口,入口流量都会被gtun进行拦截并代理到指定出口。
gtun是一款开源的ip代理加速软件目前只支持linux通过`tproxy`技术实现流量劫持,`quic``kcp`等协议优化广域网传输gtun提供一个基础通道所有加入`ipset`的ip流量都会被gtun进行拦截并代理到指定出口。
gtun支持多线路配置可以同时对美国日本欧洲目的网络进行加速访问。您可以结合dnsmasq来使用将需要配置加速的域名解析结果加入ipset从而实现域名加速。
[![](https://res.cloudinary.com/marcomontalbano/image/upload/v1686622903/video_to_markdown/images/youtube--pxv02e5EXPE-c05b58ac6eb4c4700831b2b3070cd403.jpg)](https://www.youtube.com/watch?v=pxv02e5EXPE "")
**使用场景**
- SaaS软件加速加速访问Salesforceoffce365等产品
- 云服务器加速,加速访问海外服务器,跳板机,提升操作流畅度
- 直播加速tiktok海外直播加速抖音直播加速
- 游戏加速,结合专线网络和路由盒子实现游戏加速盒
gtun是一个完整的加速器**目前只支持linux**
同时我们也基于gtun开发了收费版本对标阿里云的全球应用加速ucloud的pathX等产品的功能只是会更加灵活支持私有化部署独立部署可以下沉到办公室如果您感兴趣可以访问[我们的网站](https://www.beyondnetwork.net)进行免费免费体验。
同时我们也基于gtun开发了收费版本对标阿里云的全球应用加速ucloud的pathX等产品的功能只是会更加灵活支持私有化部署独立部署可以部署到公有云数据中心和软路由如果您感兴趣可以访问[我们的网站](https://www.beyondnetwork.net)进行免费免费体验。
关于项目有任何问题需要咨询,可以[联系作者](#关于作者)进行交流
## 目录
- [介绍](#gtun)
- [应用场景](#应用场景)
- [功能特性](#功能特性)
- [技术原理](#技术原理)
- [安装部署](#安装部署)
@@ -42,10 +33,24 @@ gtun是一个完整的加速器**目前只支持linux**
- [安装运行gtun](#安装运行gtun)
- [配置加速ip](#配置加速ip)
- [加速效果测试](#加速效果)
- [应用场景](#应用场景)
- [用法玩法]()
- [基础用法: 基于gtun+ipset实现ip代理加速和分流](doc/基础用法:基于gtun+ipset实现ip代理加速和分流.md)
- [基础用法: 基于gtun+dnsmasq实现域名代理加速和分流](doc/基础用法:基于gtun+dnsmasq实现域名代理加速和分流.md)
- [基础用法: openwrt搭载gtun打造加速软路由连接Wi-Fi即可畅游网络](doc/基础用法:openwrt搭载gtun打造加速软路由连接Wi-Fi即可畅游网络.md)
- [基础用法: 基于gtun实现公有云访问外部加速](doc/基础用法:基于gtun实现公有云访问外部加速.md)
- [玩转N1盒子基于gtun实现的tiktok加速路由](doc/玩转N1盒子:基于gtun实现的tiktok加速路由.md)
- [玩转N1盒子基于gtun实现的游戏加速盒](doc/玩转N1盒子:基于gtun实现的游戏加速盒.md)
- [有问题怎么办](#有问题怎么办)
- [关于作者](#关于作者)
## 应用场景
- SaaS软件加速加速访问Salesforceoffce365等产品
- 云服务器加速,加速访问海外服务器,跳板机,提升操作流畅度
- 直播加速tiktok海外直播加速抖音直播加速
- 游戏加速,结合专线网络和路由盒子实现游戏加速盒
- 云服务器出口加速网关,加速整个公有云内网访问外网的流量
## 功能特性
- 纯应用层实现不存在overlay网络支持tcp和udp协议以及运行在其上的所有七层协议
@@ -64,9 +69,9 @@ gtun是一款ip正向代理软件包含代理客户端gtun和服务端gtund
gtun最主要的功能是流量代理gtun经过三个版本的演变最初基于tun网卡的vpn技术然后优化到dnat技术再到目前的tproxy技术现已逐步趋于稳定。
gtun本身只提供流量代理通道至于哪些流量需要被劫持这个是由使用者定义的使用者最终只需要将被代理的IP加入到`ipset`当中那么该ipset的ip就会被代理
gtun本身只提供流量代理通道至于哪些流量需要被劫持**这个是由使用者定义的**使用者最终只需要将被代理的IP加入到`ipset`当中那么该ipset的ip就会被代理
为了实现更加快速的代理gtun考虑集成`kcp`或者`quic`等基于UDP实现的可靠性传输协议以避免长链路tcp丢包严重触发拥塞控制机制降低传输效率。
为了实现更加快速的代理gtun考虑集成`kcp`或者`quic`等基于UDP实现的可靠性传输协议同时接入FEC实时选路等机制以避免长链路tcp丢包严重触发拥塞控制机制降低传输效率。
[返回目录](#目录)
@@ -76,87 +81,110 @@ gtun本身只提供流量代理通道至于哪些流量需要被劫持
### 前期准备
- 一台公有云服务器用于部署服务端程序gtund区域越靠近被加速区域源站越好并且确认gtund监听的端口被打开
- 另外一台可以是公有云服务器也可以是内网机器用于部署客户端程序gtun目前gtun只支持linux系统
- 另外一台可以是公有云服务器,也可以是内网机器,也可以是路由器,用于部署客户端程序gtun目前gtun只支持linux系统
### 安装运行gtund
gtund需要运行在公有云上,相对比较简单,原则上越靠近源站越好
gtund部署在美国的AWS上支持systemd和docker两种方式进行启动
首先生成配置文件,可以下载 [gtund.yaml](https://github.com/ICKelin/gtun/blob/master/etc/gtund.yaml) 进行修改
在[release](https://github.com/ICKelin/gtun/releases)里面找到2.0.7版本的产物并进行下载,
```
cd gtund
./install.sh
```
install.sh 会创建gtund的运行目录并通过systemd把gtund程序拉起。
执行install.sh完成之后gtund会
- 监听tcp的3002作为mux协议的服务端口
- 监听udp的3002作为kcp协议的服务端口
- 日志记录在/opt/apps/gtund/logs/gtund.log
gtund的默认配置为默认情况下不需要作任何的修改即可
```yaml
enable_auth: true
auths:
- access_token: "ICKelin:free"
expired_ath: 0
trace: ":3003"
server:
- listen: ":3002"
authKey: "rewrite with your auth key"
scheme: "kcp"
- listen: ":3002"
scheme: "mux"
log:
days: 5
level: "debug"
path: "gtund.log"
path: "/opt/apps/gtund/logs/gtund.log"
```
大部分情况下,如果您的端口未被占用,不需要修改任何配置
`./gtund -c gtund.yaml`文件即可。
您也可以使用docker-compose来进行安装
```shell
cd gtund
docker-compose up --build -d
```
执行完之后docker ps 看是否启动成功
### 安装运行gtun
gtun可以运行在内网也可以运行在公有云在本场景当中gtun会被部署在内网。
首先生成配置文件,可以下载 [gtun.yaml](https://github.com/ICKelin/gtun/blob/master/etc/gtun.yaml) 进行修改
gtun的安装也类似在[release](https://github.com/ICKelin/gtun/releases)里面找到2.0.7版本的产物并进行下载然后在本地linux上进行部署
```yaml
settings:
US:
# 代理ip文件可以是本地文件也可以是网络文件一行是一个IP或者cidr
proxy_file: "https://www.ipdeny.com/ipblocks/data/countries/us.zone"
route:
# 拨测地址需要修改US_SERVER_IP和US_SERVER_TRACE_PORT对应gtund的公网IP和端口
- trace_addr: ${US_SERVER_IP}:${US_SERVER_TRACE_PORT}
scheme: "kcp"
# 服务端地址修改为对应gtund的IP和端口
addr: ${US_SERVER_IP}:${US_SERVER_PORT}
auth_key: "rewrite with your auth key"
proxy:
# 代理插件配置
"tproxy_tcp": |
{
"read_timeout": 30,
"write_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "US"
}
"tproxy_udp": |
{
"read_timeout": 30,
"write_timeout": 30,
"session_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "US"
}
log:
days: 5
level: Debug
path: gtun.log
http_server:
listen_addr: ":9001"
```shell
cd gtun
export ACCESS_TOKEN="ICKelin:free"
export SERVER_IP="gtund所在的服务器的ip"
./install.sh
```
配置完成之后可以启动gtun程序运行`./gtun -c gtun.yaml`即可启动。
其中ACCESS_TOKEN为gtund配置的认证的tokenSERVER_IP是gtund的公网IP
gtund启动时会自动设置 iptables规则和路由表并将需要加速的ip加入ipset当中如果ip量比较大启动时间会稍微长一些。
安装完成之后查看是否有错误日志
同样你也可以使用docker-compose来安装
```shell
cd gtun
docker-compose up --build -d
```
执行完成之后docker ps 看是否启动成功。
[返回目录](#目录)
### 配置加速ip
目前支持两种方式配置IP
- 基于接口的方式我们提供HTTP接口进行动态增删IP目前正在开发页面配置动态管理加速的IP应用域名敬请期待
- 使用命令手动配置手动将ip加入到ipset当中
在上述过程中启动了gtun和gtund程序但是并未添加任何需要加速的信息那么gtun如何进行加速呢需要额外手动配置加速ip并将该ip的tcp流量全部转发至127.0.0.1:8524端口udp流量全部转发至127.0.0.1:8524∂端口
这个过程是通过ipset和路由来配置的。以1.1.1.1为例
第一步创建ipset并将1.1.1.1加入其中
```
ipset create GTUN-US hash:net
ipset add GTUN-US 1.1.1.1
```
第二步创建iptables规则匹配目的ip为GTUN-US这个ipset内部的ip然后做tproxy操作将流量重定向到本地8524和8524端口
```
iptables -t mangle -I PREROUTING -p tcp -m set --match-set GTUN-US dst -j TPROXY --tproxy-mark 1/1 --on-port 8524
iptables -t mangle -I PREROUTING -p udp -m set --match-set GTUN-US dst -j TPROXY --tproxy-mark 1/1 --on-port 8524
iptables -t mangle -I OUTPUT -m set --match-set GTUN-US dst -j MARK --set-mark 1
```
第三步,添加路由表
```
ip rule add fwmark 1 lookup 100
ip ro add local default dev lo table 100
```
至此所有配置都已经完成后续需要新增代理ip只使用以下命令将ip加入GTUN-US这个ipset当中即可现在可以先尝试测试1.1.1.1这个ip的代理。
接下来以命令配置的方式进行配置,以`1.1.1.1`为例,只需要将`1.1.1.1`加入其中ipset当中`ipset add GTUN-US 1.1.1.1`即可。
```
root@raspberrypi:/home/pi# nslookup www.google.com 1.1.1.1
Server: 1.1.1.1
@@ -211,13 +239,6 @@ root@raspberrypi:/home/pi# wget http://speedtest.atlanta.linode.com/100MB-atlant
[返回目录](#目录)
## 应用场景
- IP加速可用于ip子网加速
- 域名站点加速需要使用dnsmasq或者nginx/openresty等组件实现
- k8s集群网络代理ip加速的一个子集可代理访问k8s的servicepod网段
- 全球应用加速
## 有问题怎么办
- [wiki](https://github.com/ICKelin/gtun/wiki)

24
build.sh Executable file
View File

@@ -0,0 +1,24 @@
rm -r release
mkdir -p release
mkdir -p release/gtun/etc
mkdir -p release/gtund/etc
git log -n 5 > release/gtun/ChangeLog
git log -n 5 > release/gtund/ChangeLog
DIR=`pwd`
cd src/gtun
GOOS=linux go build -o gtun
mv gtun $DIR/release/gtun/
cd $DIR
cp scripts/install_gtun.sh release/gtun/install.sh
cp -r scripts release/gtun/scripts
cp -r etc/gtun/* release/gtun/etc
cd src/gtund
GOOS=linux GOARCH=amd64 go build -o gtund
mv gtund $DIR/release/gtund/
cd $DIR
cp scripts/install_gtund.sh release/gtund/install.sh
cp -r etc/gtund/* release/gtund/etc

View File

@@ -1,14 +0,0 @@
#!/bin/bash
echo "building gtund...."
GOOS=linux go build -o bin/gtund/gtund cmd/gtund/*.go
echo "builded gtund...."
echo "building gtun...."
GOOS=linux go build -o bin/gtun/gtun-linux_amd64 cmd/gtun/*.go
GOARCH=arm GOOS=linux go build -o bin/gtun/gtun-linux_arm cmd/gtun/*.go
echo "builded gtun...."
cp -r etc/gtun.yaml bin/gtun/
cp -r etc/gtund.yaml bin/gtund/
cp install.sh bin/gtun/

View File

@@ -1,14 +0,0 @@
WORKSPACE=`pwd`
./build_exec.sh
echo "building gtund docker image"
cd docker-build/gtund
cp $WORKSPACE/bin/gtund/gtund .
docker build . -t gtund
echo "builded gtund docker image"
echo "building gtun docker image"
cd $WORKSPACE/docker-build/gtun
cp $WORKSPACE/bin/gtun/gtun-linux_amd64 .
docker build . -t gtun
echo "builded gtun docker image"

View File

@@ -1,42 +0,0 @@
/*
MIT License
Copyright (c) 2018 ICKelin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
DESCRIPTION:
This program is a gtun client for game/ip accelator.
Author: ICKelin
*/
package main
import (
"github.com/ICKelin/gtun/gtun"
)
func main() {
gtun.Main()
}

View File

@@ -1,40 +0,0 @@
/*
MIT License
Copyright (c) 2018 ICKelin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
DESCRIPTION:
This program is a gtun server for game/ip accelator.
Author: ICKelin
*/
package main
import "github.com/ICKelin/gtun/gtund"
func main() {
gtund.Main()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

3
doc/softroute.md Normal file
View File

@@ -0,0 +1,3 @@
# 基于gtun+n1盒子实现软路由加速

10
doc/tk.md Normal file
View File

@@ -0,0 +1,10 @@
IP选择
- 我有IP需要转发加速
- 我没有IP需要增加IP
线路模式:
- 直播线路
- 养号线路
tk区域

View File

@@ -0,0 +1,65 @@
# openwrt搭载gtun打造加速软路由连接Wi-Fi即可畅游网络
之前两篇文章都介绍的是如何加速本机的但是实际过程中我们希望能够连接上Wi-Fi就能够进行访问加速这个就需要用到软路由。
本文将详细介绍如何打造一个基于gtun加速的软路由最终实现的效果是家里的任何一台设备只要连上Wi-Fi不需要任何配置就能够实现加速。
依照之前文章的习惯,我们首先来看一下本文的拓扑:
![img.png](assets/softroute_acc_topology.png)
如图所示:
- N1盒子网上很多很便宜就能买到买了之后自己刷入openwrtN1盒子有无线Wi-Fi功能不需要配置旁路由只要连上N1盒子的Wi-Fi就行
- gtun部署在N1盒子上由于gtun是基于iptables来匹配流量转发的因此能够实现
- 本机流量劫持和加速
- 作为路由器时,经过本机路由转发的流量的加速
- 除了N1盒子和gtun之外由于openwrt本身就已经刷入dnsmasq了这里不再多说dnsmasq的域名策略还是沿用[之前文章](基础用法:基于gtun+dnsmasq实现域名代理加速和分流.md)的配置。
# N1盒子刷入Openwrt
- N1盒子是从淘宝购买的
- openwrt我刷入的是[这个固件](https://github.com/ophub/flippy-openwrt-actions/releases/download/OpenWrt_lede_save_2024.04/openwrt_s905d_n1_R24.2.2_k5.15.152-flippy-88+o.img.gz)
操作很简单下载刷入u盘之后插入N1盒子已经设置自动u盘启动
然后执行install-to-emmc.sh选择11刷入emmc成功之后拔掉u盘重启进入openwrt。
操作完成之后你会得到以下信息:
- Wi-Fi名称Phnicomm_n1
- Wi-Fi密码password
- 终端用户名root
- 终端密码password
- Wi-Fi网段192.168.1.1/24
接下来需要配置上网:
- 创建wan口并且桥接到eth1
![img.png](assets/softroute_acc_step1.png)
![img_1.png](assets/softroute_acc_step2.png)
- 去掉lan口的桥接
![img_2.png](assets/softroute_acc_step3.png)
- 开机自启脚本加入两条命令
```shell
iptables -t filter -P FORWARD ACCEPT
iptables -t filter -I FORWARD -j ACCEPT
```
![img_3.png](assets/softroute_acc_step4.png)
操作完成之后就可以通过连接N1盒子的Wi-Fi上网了。
N1盒子本身有两个服务监听的udp 53端口我们需要把named服务停掉避免解析时候不知道使用的是哪个服务解析。
![img_4.png](assets/softroute_acc_step5.png)
# 部署程序
部署程序和配置没有太多特别之处,参考这两篇文章进行:
- [基础用法:基于gtun+ipset实现ip代理加速和分流](./基础用法:基于gtun+ipset实现ip代理加速和分流.md)
- [基础用法:基于gtun+dnsmasq实现域名代理加速和分流](./基础用法:基于gtun+dnsmasq实现域名代理加速和分流.md)
# 结束语
完成上面两个步骤之后就能连接N1盒子的Wi-Fi实现相比较之前的而言除了搭载硬件不一样其他的没有太大的区别配置几乎是一样的。
gtun的灵活之处在于它提供的是一个基础功能不关注你底层跑的硬件是什么
底层的线路是什么转发规则怎么配置只要你按照我们约定好的ipset进行配置就无脑转发。

View File

@@ -0,0 +1,84 @@
# 基于gtun+dnsmasq实现域名代理加速和分流.md
之前的文章分享了[使用gtun实现ip代理加速和分流](./基础用法:基于gtun+ipset实现ip代理加速和分流.md)实现了一个最简单的基于ip代理加速的场景但是在实际应用当中会有两个不太优雅的地方
- 基于ip的方式如果ip发生变动可能会出现分流策略不准的问题
- 有时候并不需要加速这么多ip只需要加速部分网站或者应用即可非常典型的比如SaaS应用加速
- 考虑一下真正使用的时候dns应该如何配置如果配置成114.114.114.114那么需要通过8.8.8.8来进行解析的ip则会被污染如果设置成8.8.8.8那么国内的网站可能会解析到国外的ip这也算是一种dns污染
基于此我们有了基于dnsmasq的域名解析策略来实现基于域名的加速和分流希望能够解决这三个问题最终拓扑如下
![img.png](assets/domain_acc_topology.png)
# 前置准备
您可以参考[这篇文章](基础用法:基于gtun+ipset实现ip代理加速和分流.md)来安装gtund和gtun。安装完gtun和gtund之后您需要再安装dnsmasq并且成功启动。
# 配置dnsmasq解析策略
首先还是创建好基本的运行环境,参考`gtun/scripts/redirect_all.sh`
然后配置dnsmasq的规则dnsmasq的规则主要有两个
- 域名解析的上游地址是多少针对大陆地区的域名使用114.114.114.114来进行解析针对其他域名使用8.8.8.8进行解析
- 解析接入写入到哪个ipset里面针对大陆地区的域名写入到NOPROXY当中海外域名不进行处理因为在GTUN_ALL里面已经匹配除了NOPROXY之外的所有ip
通过这两个控制我们就能够实现dnsmasq和gtun的无缝结合双方都不需要感知对方的存在dnsmasq只管结果写进ipsetgtun只管匹配ipset通过ipset来实现进程之间的默契。
```shell
config_dnsmasq() {
echo "configuring dnsmasq service"
cp dnsmasq/dnsmasq.conf /etc/dnsmasq.conf
cp dnsmasq/dnsmasq.resolv /etc/dnsmasq.resolv
echo "configuring dnsmasq cn domain list"
cp dnsmasq/cn.conf /etc/dnsmasq.d/
cp dnsmasq/cn_set.conf /etc/dnsmasq.d/
systemctl restart dnsmasq
}
```
完整命令可以参考`gtun/scripts/redirect_domains.sh`。修改完之后本机需要设置`/etc/resolv.conf`文件的`nameserver 127.0.0.1`
只有这样才会真正用本机的dnsmasq去解析。后面我会介绍软路由的方式让其他机器通过配置好gtun的软路由也能使用这种代理和分流的策略。
# 测试
接下来进行一轮测试,我们使用我们自己的一个域名`demo.xxxx.tech`进行测试。
第一步将demo.xxxx.tech配置进dnsmasq里面
```shell
root@OpenWrt:~/gtun# head /etc/dnsmasq.d/cn.conf
server=/demo.xxxx.tech/114.114.114.114
root@OpenWrt:~/gtun# head /etc/dnsmasq.d/cn_set.conf
ipset=/demo.xxxx.tech/NOPROXY
```
第二步nslookup解析测试
```shell
root@OpenWrt:~/gtun# nslookup demo.xxxx.tech 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1:53
Non-authoritative answer:
Name: demo.xxxx.tech
Address: 47.115.xx.xx
Non-authoritative answer:
root@OpenWrt:~/gtun# ipset -T NOPROXY 47.115.xx.xx
Warning: 47.115.xx.xx is in set NOPROXY.
```
demo.xxxx.tech这个域名已经被加入到NOPROXY里面了根据之前的文章加入到NOPROXY之后不会再走加速出口出这里不再赘述了。
# 结束语
最后我们回到文章一开始提到的三个问题。
首先是ip不准的问题本文通过域名来实现控制不是事先写入固定的cidr列表来判断的。
其次是部分加速的需求这个也同样可以综合域名和IP来实现
最后是dns应该用哪个本文已经给出了答案把dns设置为127.0.0.1交给dnsmasq来判断应该用`114.114.114.114`还是`8.8.8.8`
截止目前位置我们已经了解到了ip加速和域名加速但是所有的加速都是加速本机的流量接下来我会结合 软路由的方式详细说明如何实现连接Wi-Fi就能实现gtun的加速。

View File

@@ -0,0 +1,214 @@
# 最佳实践: 基于gtun+ipset实现ip代理加速和分流
gtun的基础功能是ip加速本文通过具体的配置来讲解如何配置gtun的ip加速功能最终实现的效果是除了局域网IP之外访问其他所有IP都通过gtun转发到美国出口。
最终拓扑如下:
![img.png](assets/ip_acc_topology.png)
如图所示,本文会包含两个部分:
- 本地gtun美国gtund的部署
- 加速流量和非加速流量的区分这部分通过ipset和iptables来进行
我们可以通过iptables非常灵活的控制加速和非加速流量。
**本文是基于gtun的2.0.7版本。**
# 安装
安装包括两个组件:
- gtundip加速的服务端程序部署在美国
- gtunip加速的客户端程序部署在本地linux
## 安装gtund
gtund部署在美国的AWS上支持systemd和docker两种方式进行启动。
在[release](https://github.com/ICKelin/gtun/releases)里面找到2.0.7版本的产物并进行下载,
```
cd gtund
./install.sh
```
install.sh 会创建gtund的运行目录并通过systemd把gtund程序拉起。
执行install.sh完成之后gtund会
- 监听tcp的3002作为mux协议的服务端口
- 监听udp的3002作为kcp协议的服务端口
- 日志记录在/opt/apps/gtund/logs/gtund.log
gtund的默认配置为默认情况下不需要作任何的修改即可
```yaml
enable_auth: true
auths:
- access_token: "ICKelin:free"
expired_ath: 0
trace: ":3003"
server:
- listen: ":3002"
scheme: "kcp"
- listen: ":3002"
scheme: "mux"
log:
days: 5
level: "debug"
path: "/opt/apps/gtund/logs/gtund.log"
```
您也可以使用docker-compose来进行安装
```shell
cd gtund
docker-compose up --build -d
```
执行完之后docker ps 看是否启动成功
## 安装gtun
gtun的安装也类似在[release](https://github.com/ICKelin/gtun/releases)里面找到2.0.7版本的产物并进行下载然后在本地linux上进行部署
```shell
cd gtun
export ACCESS_TOKEN="ICKelin:free"
export SERVER_IP="gtund所在的服务器的ip"
./install.sh
```
其中ACCESS_TOKEN为gtund配置的认证的tokenSERVER_IP是gtund的公网IP
安装完成之后查看是否有错误日志
```shell
tail -f /opt/apps/gtun/logs/gtun.log
```
同样你也可以使用docker-compose来安装
```shell
cd gtun
docker-compose up --build -d
```
执行完成之后docker ps 看是否启动成功。
# 配置转发规则
本文的转发规则比较简单,需要加速的地址为`0.0.0.0/0`
不需要加速的地址列表在`gtun/scripts/noproxy.txt`文件里面,主要包含一些局域网地址,
需要记住的是,**一定要把gtund的公网IP加入到noproxy.txt里面**,防止自己把服务器地址拦截。
第一步把不需要的加速的地址配置好:
```shell
noproxy_set=NOPROXY
clear_noproxy() {
iptables -t mangle -D PREROUTING -m set --match-set $noproxy_set dst -j ACCEPT
iptables -t mangle -D OUTPUT -m set --match-set $noproxy_set dst -j ACCEPT
ipset destroy $noproxy_set >/dev/null
}
add_noproxy() {
ipset create $noproxy_set hash:net
cat noproxy.txt | while read line
do
echo "no proxy for" $line
ipset add $noproxy_set $line
done
iptables -t mangle -A PREROUTING -m set --match-set $noproxy_set dst -j ACCEPT
iptables -t mangle -A OUTPUT -m set --match-set $noproxy_set dst -j ACCEPT
}
clear_noproxy
add_noproxy
```
通过创建NOPROXY的ipset并且把noproxy.txt文件的cidr列表加入进去
然后通过iptables匹配到NOPROXY这个ipset的地址全部ACCEPT掉因此流量不会被劫持到gtun进程。
第二步把需要加速的地址配置好:
```shell
setname=GTUN_ALL
redirect_port=8524
add_proxy() {
ipset create $setname hash:net
echo "proxy for 0.0.0.0/1"
echo "proxy for 128.0.0.0/1"
ipset add $setname 0.0.0.0/1
ipset add $setname 128.0.0.0/1
iptables -t mangle -A PREROUTING -p tcp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -A PREROUTING -p udp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -A OUTPUT -m set --match-set $setname dst -j MARK --set-mark 1
# redirect dns query
# iptables -t mangle -A PREROUTING -p udp --dport 53 -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
# iptables -t mangle -A OUTPUT -p udp --dport 53 -j MARK --set-mark 1
ip rule add fwmark 1 lookup 100
ip ro add local default dev lo table 100
}
add_proxy
```
通过创建GTUN_ALL的ipset并且把`0.0.0.0/1``128.0.0.0/1`加入到其中。
然后通过iptables打mark和策略路由来实现访问GTUN_ALL这个ipset的cidr地址的流量的劫持。
上面两个步骤通过控制NOPROXY和GTUN_ALL两个ipset就能实现流量是否劫持到gtun即会不会被加速。
以上两个步骤的脚本已经打包进`gtun/scripts/redirect_all.sh`文件里面,有需要可以根据具体情况进行修改。
上面是实现的所有流量加速的但是有时候我们需要部分不加速比如大陆地区的ip访问不加速
那么只需要把大陆地区的ip加入到NOPROXY这一ipset即可。
```shell
wget https://raw.githubusercontent.com/herrbischoff/country-ip-blocks/master/ipv4/cn.cidr
cat cn.cidr | while read line
do
echo "no proxy for" $line
ipset add $noproxy_set $line
done
```
用法非常多,后续文章会不断分享一些用法。
# 测试
最后来进行一次简单的测试首先是不加速的验证这里我用我的一个服务器的ip来进行测试。
```shell
# 将ip加入到NOPROXY ipset当中
ipset add NOPROXY xx.xx.xx.xx
# ssh 连接ip
ssh root@xx.xx.xx.xx
# 使用who命令查看当前连接的ip
root@iZwz97kfjnf78copv1ae65Z:~# who
root tty1 Jun 28 10:41
root pts/0 Apr 28 09:43 (119.139.xx.xx)
```
最终结果走的是本地的出口(119.139.xx.xx)。
同样的方式把这个ip从NOPROXY ipset中删除加入到GTUN_ALL这个匹配走加速的ipset当中。
```shell
ipset del NOPROXY xx.xx.xx.xx
ipset add GTUN_ALL xx.xx.xx.xx
root@iZwz97kfjnf78copv1ae65Z:~# who
root tty1 Jun 28 10:41
root pts/1 Apr 28 09:46 (3.141.xx.xx)
```
最终走的是加速的出口3.141.xx.xx
# 结束语
以上是gtun的最基本的功能实现所有流量劫持并进行加速同时也提了一嘴如何访问大陆地区的ip不加速
通过本文基本上能了解gtun是如何跑起来的也能定制一些更加适合自己的用法。
后续会继续介绍如何通过gtun跟dnsmasq结合实现访问域名的加速。

View File

@@ -0,0 +1,88 @@
# 基于gtun实现公有云访问外部加速
之前的三篇教程都偏向于本地加速的,在云场景下也能实现类似的代理加速功能,公有云场景和旁路由非常类似,
本文通过配置一个部署了gtun云网关让所有vpc的访问流量都通过这台服务器作为加速网关能够实现两个基本的功能
- vpc内访问加速比如现在AI发展迅猛很多企业都需要在公有云上下载大模型那么通过配置gtun的加速网关能够解决这一场景
- 跨vpc的单边访问比如有大陆和美国两个公有云通过配置对端vpc的网段加速就能实现大陆vpc通过内网ip访问美国的vpc。
依照惯例,我们还是先看一看拓扑图。
![img.png](assets/public_cloud_topology.png)
- 包含大陆腾讯云和美国aws
- 美国aws找一台云服务器部署gtund同时内网开启一个api-server进行测试验证
- 大陆腾讯云找一台云服务器部署gtun同时内网有其他实例通过配置vpc路由把流量转到gtun所在的云服务器上
> ⚠️
> gtun的云服务器相当于一个路由器如果该云服务器宕机会影响到其他实例的网络可用性
那么接下来开始具体的配置。
# 程序安装
这个之前的文章已经详细介绍了,并没有太多的区别,可以参考下面文章来进行配置
[基础用法:基于gtun+ipset实现ip代理加速和分流](./基础用法:基于gtun+ipset实现ip代理加速和分流.md)
# 配置
首先清理GTUN_ALL配置的加速列表和NOPROXY的加速列表
```shell
ipset -F GTUN_ALL
ipset -F NOPROXY
```
这两个操作完之后就是一个不配置任何加速的空跑的gtun服务然后开始配置
配置包括两个方面:
- gtun所在云服务器的转发规则ipset操作
- 公有云的路由表
本文包括两个场景:
- 美国公有云vpc网段的加速
- 非vpc网段的加速
首先配置vpc网段的加速。
第一步把对端vpc的cidr加入到GTUN_ALL这个ipset当中
```shell
ipset add GTUN_ALL 172.31.0.0/16
```
配置完这一步之后你就可以在gtun所在的云服务器通过对端服务器的内网地址访问到对端的服务。
我们启动一个http server来进行测试
```shell
root@ip-172-31-3-157:~# python3 -m http.server 12590
# 在大陆腾讯云通过curl访问测试
root@iZwz97kfjnf78copv1ae65Z:~# curl 172.31.3.157:12590 >/dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 835 100 835 0 0 3255 0 --:--:-- --:--:-- --:--:-- 3261
# 观察输出来源IP是gtund所在服务器的内网IP172.31.3.157
Serving HTTP on 0.0.0.0 port 12590 (http://0.0.0.0:12590/) ...
172.31.3.157 - - [28/Apr/2024 10:59:31] "GET / HTTP/1.1" 200 -
```
有时候我们并不只是一台机器需要被gtun代理因此需要配置除了gtun所在的服务器之外的其他服务器的代理
这里需要通过配置公有云的vpc路由来把流量导入到gtun所在服务器。
第二步配置公有云的路由表把对端vpc的cidr路由到gtun所在的云服务器实例。
通过公有云的路由表能够实现其他机器也能够通过gtun访问到对端vpc的http服务。
非美国vpc网段的流量也是同样的配置方式只是路由表会比较大您也可以跟之前的文章一样配置类似`0.0.0.0/0`的路由把所有流量先导入到gtun所在服务器。
# 结束语
gtun加速公有云访问外部流量与其他场景没有本质上的区别唯一的区别仅仅只是需要配置公有云的路由
如果要跟软路由场景对标软路由由于本身有无线Wi-Fi因此网关会自动设置为gtun所在机器公有云则需要在云厂商控制台配置路由来控制。
本文的方式更像是旁路由的方式旁路由通常是手动修改网关地址或者通过修改dhcp下发的地址来控制流量的流向这里的公有云路由表类似dhcp下发。

View File

@@ -0,0 +1,63 @@
# 玩转N1盒子:基于gtun实现的tiktok加速路由
tiktok是目前出海比较火的一个场景这个场景有以下问题需要解决
- tiktok在国内访问不了首先需要解决访问的问题
- tiktok需要独占出口ip不然上传视频可能会0播直播可能会被封禁
- 有的客户需要用本地运营商的IP这部分我没有详细分析过不同IP带来的差异
- 客户对成本有不一样的要求对于直播客户希望用专线而且通常带宽比较固定5mbps就能够支持一个在线直播对于普通的刷视频传视频客户不一定需要专线
这些场景有的是可以通过gtun解决的有的比如运营商IP专线这类资源型的服务则是gtun程序本身解决不了的。
资源型的服务我们有企业专门提供这部分对于我们不是什么太大的问题我们比扬云专门作这种专线组网以及IP的业务感兴趣的可以到[官网](https://www.beyondnetwork.net)
了解更多。
言归正传针对tiktok加速我们提供了如下拓扑
![img.png](assets/tiktok_acc_topology.png)
- 在终端还是基于N1盒子的软路由
- 代理软件还是使用的gtun/gtund配套
- 线路分为两块
- 用于直播的专线线路
- 用于养号的纯协议优化的线路依赖的是gtun的kcp/quic这类协议
# 配置
配置上与之前[基础用法:openwrt搭载gtun打造加速软路由连接Wi-Fi即可畅游网络](基础用法:openwrt搭载gtun打造加速软路由连接Wi-Fi即可畅游网络.md)
差别不大只是底层线路上需要用到我们提供的专线出口IP上需要用到独享的IP。
# 方案的缺点
这个方案有几个非常明显的缺点:
- 从本地出去经过运营商再到海外这个走的是UDPUDP在一些地区的运营商可能会被拦截掉导致掉线
- 一个软路由同时只能使用一个IP这个是技术问题比如同时tiktok的域名是`a.b.live.tiktok.com`那么域名劫持的时候结果只会写入到其中一个ipset里面那么必然只能走一个IP出。
针对第一个问题可以考虑把udp换成tcp同时流量先经过国内公有云跳一跳上到公有云之后再继续走的UDP协议。
针对专线则没有这个问题专线包含国内和国外两个机器传输质量较好可以全程用tcp。
![img.png](assets/tiktok_acc_optimize.png)
针对第二个问题,目前使用软路由暂时没有太好的解决方式,如果需要解决这个问题,有两个思路:
- 针对不同的内网IP进行匹配
- 使用小火箭,不实用软路由了
# tiktok的产品化解决方案
我们考虑了一段时间还是考虑提供tiktok商家的解决方案这个解决方案包括
- IPIP又包括运营商IP和云厂商IP
- 代理可以基于gtun的软路由也可以基于小火箭
- 线路,这个就比较复杂
- 直连的线路可能会被gfw封禁
- 优化的线路基于gtun和gtun的商业化产品[gipa](https://www.beyondnetwork.net)来提供性价比的线路
- 部分专线线路,广港走专线,因为广港我们本身有资源,专线成本也比较低,很多客户都能接受
- 全程专线,广港专线出,然后香港再专线到对应的国家,把出境线路收敛在广港
基于此思路我们做了一套tiktok解决方案
# 结束语
tiktok目前是出海领域非常火的方向tiktok的网络解决方案是很多出海工作室的刚需我们在软件的基础之上加上自身拥有的基础设施资源可以非常快速的适应这一场景。

View File

@@ -0,0 +1,39 @@
# 基于gtun实现的游戏加速盒
之前提到tiktok出海在跨境领域还有很多场景游戏就是另外一块比较大的应用场景。游戏又可以细分为两个领域。
- ToC的游戏加速像市面上比较常见的UU雷神迅游这种除了软件之外有些还有游戏加速盒子
- ToB的游戏加速针对游戏公司游戏工作室需要海外发行游戏但是希望给国内玩家提供服务
本文主要侧重在ToC的游戏加速ToB的游戏加速有需要的客户可以先了解我们官网上的[全球加速产品](https://www.beyondnetwork.net)。
那么还是先来看看本文的一个简单拓扑。
![img.png](assets/game_acc_topology.png)
跟tiktok几乎是一模一样但是游戏场景没有tiktok的养号线路因此全程都走的专线。
游戏场景同样也有IP的问题我们对接过的游戏加速器厂商普遍都希望用本地运营商IP我们可以提供运营商IP也可以提供数据中心的IP。
同样IP可以是独享的也可以是共享的正常情况下游戏共享的IP问题也不是很大但是共享一个IP池的然后选一个线路每条线路对应一个IP。
# 游戏加速的产品化解决方案
我们的游戏解决方案提供以下服务:
- 基于gtun的流量劫持服务搭载在Openwrt上做成一个游戏加速盒子
- 提供专线加速的能力,降低丢包和延迟
- 提供多种IP类型包含运营商IP和BGP IP
以上是给普通的游戏玩家的,那么针对企业客户,我们有两个方向的合作。
- 如果您需要自己搭建一套这类游戏加速盒子,我们也可以提供技术支持和资源支持。
- 如果您是游戏工作室,可以使用我们的线路来帮您实现全球同服,国内玩家加速,公有云组网的场景
# 结束语
我们提供的所有的加速基本原理都是一样的软件层面使用gtun来进行流量劫持和代理针对不同的场景底层资源不一样。
针对普通的应用加速场景不一定需要专线只需要使用gtun然后配置kcp协议即可
针对tiktok的加速部分场景需要专线部分场景不需要专线但是对于gtun而言是透明的gtun本身不需要感知是什么业务是否使用专线
针对游戏加速场景,我们强烈推荐专线,很多游戏对延迟和丢包非常敏感,而且游戏玩家能够很清楚的感知到延迟,丢包的影响。

View File

@@ -1,65 +0,0 @@
version: '3'
services:
gtun:
build: ./gtun
container_name: gtun
restart: always
network_mode: host
privileged: true
volumes:
- /opt/apps/logs:/opt/logs
environment:
TIME_ZONE: Asia/Shanghai
settings: |
settings:
CN:
proxy_file: "https://www.ipdeny.com/ipblocks/data/countries/us.zone"
route:
- trace_addr: ${CN_SERVER_IP}:${CN_SERVER_TRACE_PORT}
scheme: "kcp"
addr: ${CN_SERVER_IP}:${CN_SERVER_PORT}
auth_key: ""
proxy:
"tproxy_tcp": |
{
"read_timeout": 30,
"write_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "CN"
}
"tproxy_udp": |
{
"read_timeout": 30,
"write_timeout": 30,
"session_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "CN"
}
log:
days: 5
level: Debug
path: gtun.log
http_server:
listen_addr: ":9001"
gtund:
build: ./gtund
container_name: gtund
restart: always
network_mode: host
volumes:
- /opt/apps/logs:/logs
environment:
TIME_ZONE: Asia/Shanghai
settings: |
server:
- listen: ":3002"
authKey: "rewrite with your auth key"
scheme: "kcp"
trace: ":3003"
log:
days: 5
level: "debug"
path: "gtund.log"

2
docker-build/gtun/.env Normal file
View File

@@ -0,0 +1,2 @@
ACCESS_TOKEN=ICKelin:free
SERVER_IP=xxx.xxx.xxx.xxx

View File

@@ -1,6 +1,6 @@
FROM ubuntu:18.04
COPY gtun-linux_amd64 /gtun
FROM ubuntu:20.04
RUN mkdir -p /opt/apps/gtun/logs
COPY . /opt/apps/gtun
COPY start.sh /
RUN chmod +x start.sh && chmod +x gtun
RUN mkdir /opt/logs
RUN chmod +x start.sh && chmod +x /opt/apps/gtun/gtun
CMD /start.sh

View File

@@ -0,0 +1,14 @@
version: '3'
services:
accelerator:
build: .
container_name: gtun_2.0.7
restart: always
network_mode: host
privileged: true
volumes:
- /opt/apps/gtun/logs:/opt/apps/gtun/logs
environment:
TIME_ZONE: Asia/Shanghai
ACCESS_TOKEN: $ACCESS_TOKEN
SERVER_IP: $SERVER_IP

View File

@@ -3,9 +3,4 @@ if [ "$TIME_ZONE" != "" ]; then
ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone
fi
#项目的配置文件
if [ "$settings" != "" ]; then
echo "$settings" > /gtun.yaml
fi
/gtun -c /gtun.yaml
/opt/apps/gtun/gtun -c /opt/apps/gtun/etc/gtun.yaml

View File

@@ -1,6 +1,6 @@
FROM ubuntu:18.04
COPY gtund /
FROM ubuntu:20.04
RUN mkdir -p /opt/apps/gtund/logs
COPY . /opt/apps/gtund
COPY start.sh /
RUN chmod +x start.sh && chmod +x gtund
RUN mkdir /opt/logs
RUN chmod +x start.sh && chmod +x /opt/apps/gtund/gtund
CMD /start.sh

View File

@@ -0,0 +1,12 @@
version: '3'
services:
accelerator:
build: .
container_name: gtund
restart: always
network_mode: host
privileged: true
volumes:
- /opt/apps/gtund/logs:/opt/apps/gtund/logs
environment:
TIME_ZONE: Asia/Shanghai

View File

@@ -3,9 +3,4 @@ if [ "$TIME_ZONE" != "" ]; then
ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone
fi
#项目的配置文件
if [ "$settings" != "" ]; then
echo "$settings" > /gtund.yaml
fi
/gtund -c /gtund.yaml
/opt/apps/gtund/gtund -c /opt/apps/gtund/etc/gtund.yaml

11
docker_build.sh Executable file
View File

@@ -0,0 +1,11 @@
./build.sh
rm -r images
mkdir -p images/gtun
cp -r release/gtun/* images/gtun/
cp -r docker-build/gtun/* images/gtun/
mkdir -p images/gtund
cp -r release/gtund/* images/gtund
cp -r docker-build/gtund/* images/gtund/

View File

@@ -1,33 +0,0 @@
settings:
CN:
proxy_file: "https://www.ipdeny.com/ipblocks/data/countries/us.zone"
route:
- trace_addr: ${CN_SERVER_IP}:${CN_SERVER_TRACE_PORT}
scheme: "kcp"
addr: ${CN_SERVER_IP}:${CN_SERVER_PORT}
auth_key: ""
proxy:
"tproxy_tcp": |
{
"read_timeout": 30,
"write_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "CN"
}
"tproxy_udp": |
{
"read_timeout": 30,
"write_timeout": 30,
"session_timeout": 30,
"listen_addr": ":8524",
"rate_limit": 50,
"region": "CN"
}
log:
days: 5
level: Debug
path: gtun.log
http_server:
listen_addr: ":9001""

16
etc/gtun/gtun.service Normal file
View File

@@ -0,0 +1,16 @@
[Unit]
Description=gtun - ip accelerator base on tproxy
After=network.target auditd.service
[Service]
ExecStart=/opt/apps/gtun/gtun -c /opt/apps/gtun/etc/gtun.yaml
KillMode=process
Restart=always
RestartPreventExitStatus=255
Type=simple
LimitNOFILE=1000000
LimitNPROC=1000000
[Install]
WantedBy=multi-user.target
Alias=gtun.service

28
etc/gtun/gtun.yaml Normal file
View File

@@ -0,0 +1,28 @@
access_token: "${ACCESS_TOKEN}"
accelerator:
HK:
routes:
- scheme: "kcp"
server: "${SERVER_IP}:3002"
trace: "${SERVER_IP}:3003"
- scheme: "mux"
server: "${SERVER_IP}:3002"
trace: "${SERVER_IP}:3003"
proxy:
tproxy_tcp: |
{
"read_timeout": 30,
"write_timeout": 30,
"listen_addr": ":8524"
}
tproxy_udp: |
{
"read_timeout": 30,
"write_timeout": 30,
"session_timeout": 30,
"listen_addr": ":8524"
}
log:
days: 5
level: debug
path: /opt/apps/gtun/logs/gtun.log

View File

@@ -1,14 +0,0 @@
trace: ":3003"
server:
- listen: ":3002"
authKey: "rewrite with your auth key"
scheme: "kcp"
- listen: ":3001"
authKey: "rewrite with your auth key"
scheme: "mux"
log:
days: 5
level: "debug"
path: "gtund.log"

16
etc/gtund/gtund.service Normal file
View File

@@ -0,0 +1,16 @@
[Unit]
Description=gtund - ip accelerator base on tproxy
After=network.target auditd.service
[Service]
ExecStart=/opt/apps/gtund/gtund -c /opt/apps/gtund/etc/gtund.yaml
KillMode=process
Restart=always
RestartPreventExitStatus=255
Type=simple
LimitNOFILE=1000000
LimitNPROC=1000000
[Install]
WantedBy=multi-user.target
Alias=gtund.service

17
etc/gtund/gtund.yaml Normal file
View File

@@ -0,0 +1,17 @@
enable_auth: true
auths:
- access_token: "ICKelin:free"
expired_ath: 0
trace: ":3003"
server:
- listen: ":3002"
scheme: "kcp"
- listen: ":3002"
scheme: "mux"
log:
days: 5
level: "debug"
path: "/opt/apps/gtund/logs/gtund.log"

24
go.mod
View File

@@ -1,10 +1,10 @@
module github.com/ICKelin/gtun
go 1.16
go 1.20
require (
github.com/ICKelin/optw v0.0.0-20211219021958-28f4d1f075ef
github.com/agiledragon/gomonkey/v2 v2.10.1
github.com/ICKelin/optw v0.0.0-20240428102250-612f5bb01303
github.com/agiledragon/gomonkey/v2 v2.11.0
github.com/astaxie/beego v1.12.3
github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58
@@ -13,3 +13,21 @@ require (
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/klauspost/reedsolomon v1.11.7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/xtaci/kcp-go v5.4.20+incompatible // indirect
github.com/xtaci/smux v1.5.24 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
)

563
go.sum
View File

@@ -1,60 +1,15 @@
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.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
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/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/ICKelin/gtun v1.0.5-0.20211204061645-ed01445bf708/go.mod h1:lXqvFm0rxLK0HAWUJYEbrkfStUJsamURyjoIelB5FHE=
github.com/ICKelin/optw v0.0.0-20211219021958-28f4d1f075ef h1:f2j59p1x1OG/73AaAYy3nxMCfaGilxTQdOqkueaF4FQ=
github.com/ICKelin/optw v0.0.0-20211219021958-28f4d1f075ef/go.mod h1:G3/YSddzYP9BhFIiEeXR3H5Q5s4ykReqromA4w47fH8=
github.com/ICKelin/optw v0.0.0-20240428102250-612f5bb01303 h1:+Ny6mSEahFVfuEXXzoxChuWUStKyW0V53wTA/v0r3ug=
github.com/ICKelin/optw v0.0.0-20240428102250-612f5bb01303/go.mod h1:MQl8fLDhV0btvBztH05wsDamj0Rn8BDnDmCPeuIBpbY=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/agiledragon/gomonkey/v2 v2.10.1 h1:FPJJNykD1957cZlGhr9X0zjr291/lbazoZ/dmc4mS4c=
github.com/agiledragon/gomonkey/v2 v2.10.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/agiledragon/gomonkey/v2 v2.11.0 h1:5oxSgA+tC1xuGsrIorR+sYiziYltmJyEZ9qA25b6l5U=
github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
@@ -64,48 +19,22 @@ github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHK
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
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/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
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/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -113,122 +42,42 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
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/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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.3/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/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/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -236,75 +85,44 @@ 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/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcloughlin/avo v0.0.0-20201216231306-039ef47f4f69 h1:U3a/eCFK1x5LmMPHYND8zfvAa1NS8pVK60UblgsTwmA=
github.com/mmcloughlin/avo v0.0.0-20201216231306-039ef47f4f69/go.mod h1:6aKT4zZIrpGqB3RpFU14ByCSSyKY6LfJz4J/JJChHfI=
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-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -313,22 +131,12 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
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 v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
@@ -342,417 +150,78 @@ github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqI
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
github.com/xtaci/smux v1.5.15 h1:6hMiXswcleXj5oNfcJc+DXS8Vj36XX2LaX98udog6Kc=
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
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.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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/lint v0.0.0-20210508222113-6edffad5e616/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/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
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/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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-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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
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/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/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-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-20200615200032-f1bc736245b1/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-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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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/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-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-20190328211700-ab21143f2384/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-20191112195655-aa38f8e97acc/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-20200619180055-7c47624df98f/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-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
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-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-20200513103714-09dca8ec2884/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-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
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.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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
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.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -1,61 +0,0 @@
package gtun
import (
"gopkg.in/yaml.v2"
"os"
)
var gConfig *Config
type Config struct {
Settings map[string]RegionConfig `yaml:"settings"`
HTTPServer HTTPConfig `yaml:"http_server"`
Log Log `yaml:"log"`
}
type HTTPConfig struct {
ListenAddr string `yaml:"listen_addr"`
}
type RegionConfig struct {
Route []RouteConfig `yaml:"route"`
ProxyFile string `yaml:"proxy_file"`
Proxy map[string]string `yaml:"proxy"`
}
type RouteConfig struct {
Region string `yaml:"region"`
TraceAddr string `yaml:"trace_addr"`
Scheme string `yaml:"scheme"`
Addr string `yaml:"addr"`
AuthKey string `yaml:"auth_key"`
}
type Log struct {
Days int64 `yaml:"days"`
Level string `yaml:"level"`
Path string `yaml:"path"`
}
func ParseConfig(path string) (*Config, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return ParseBuffer(content)
}
func ParseBuffer(content []byte) (*Config, error) {
conf := Config{}
err := yaml.Unmarshal(content, &conf)
if err != nil {
return nil, err
}
gConfig = &conf
return &conf, err
}
func GetConfig() *Config {
return gConfig
}

View File

@@ -1,115 +0,0 @@
package gtun
import (
"encoding/json"
"github.com/ICKelin/gtun/gtun/proxy"
"io"
"net/http"
)
type HTTPServer struct {
listenAddr string
}
func NewHTTPServer(listenAddr string) *HTTPServer {
return &HTTPServer{listenAddr: listenAddr}
}
func (s *HTTPServer) ListenAndServe() error {
http.HandleFunc("/meta", loadMeta)
http.HandleFunc("/ip/add", addIP)
http.HandleFunc("/ip/delete", delIP)
return http.ListenAndServe(s.listenAddr, nil)
}
type response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func loadMeta(w http.ResponseWriter, r *http.Request) {
regionList := make([]string, 0)
regions := GetConfig().Settings
for region, _ := range regions {
regionList = append(regionList, region)
}
type replyBody struct {
Regions []string `json:"regions"`
Cfg *Config
}
body := &replyBody{
Regions: regionList,
Cfg: GetConfig(),
}
reply(w, body)
}
func addIP(w http.ResponseWriter, r *http.Request) {
type req struct {
Region string `json:"region"`
IP string `json:"ip"`
}
var form = req{}
err := bindForm(r, &form)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// add to ipset
err = proxy.AddIP(form.Region, form.IP)
if err != nil {
reply(w, &response{
Code: -1,
Message: err.Error(),
Data: nil,
})
return
}
reply(w, &response{Code: 0, Message: "success"})
}
func delIP(w http.ResponseWriter, r *http.Request) {
type req struct {
Region string `json:"region"`
IP string `json:"ip"`
}
var form = req{}
err := bindForm(r, &form)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
// delete from ipset
err = proxy.DelIP(form.Region, form.IP)
if err != nil {
reply(w, &response{
Code: -1,
Message: err.Error(),
Data: nil,
})
return
}
reply(w, &response{Code: 0, Message: "success"})
}
func bindForm(r *http.Request, obj interface{}) error {
cnt, err := io.ReadAll(r.Body)
if err != nil {
return err
}
return json.Unmarshal(cnt, obj)
}
func reply(w http.ResponseWriter, obj interface{}) {
buf, _ := json.Marshal(obj)
_, _ = w.Write(buf)
}

View File

@@ -1,52 +0,0 @@
package gtun
import (
"flag"
"fmt"
"github.com/ICKelin/gtun/gtun/proxy"
"github.com/ICKelin/gtun/gtun/route"
"github.com/ICKelin/gtun/internal/logs"
)
func Main() {
flgConf := flag.String("c", "", "config file")
flag.Parse()
conf, err := ParseConfig(*flgConf)
if err != nil {
fmt.Printf("load config fail: %v\n", err)
return
}
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
// run proxy
for region, cfg := range conf.Settings {
// init plugins
err = proxy.Setup(region, cfg.ProxyFile, cfg.Proxy)
if err != nil {
fmt.Printf("set proxy fail: %v\n", err)
return
}
}
// run route and race
raceManager := route.GetTraceManager()
for region, cfg := range conf.Settings {
raceTargets := make([]string, 0)
for _, r := range cfg.Route {
raceTargets = append(raceTargets, r.TraceAddr)
hopConn, err := route.CreateConnection(region, r.Scheme, r.Addr, r.AuthKey)
if err != nil {
fmt.Printf("connect to %s://%s fail: %v\n", r.Scheme, r.Addr, err)
return
}
go hopConn.ConnectNextHop()
}
regionRace := route.NewTrace(region, raceTargets)
raceManager.AddRegionTrace(region, regionRace)
}
raceManager.RunRace()
panic(NewHTTPServer(conf.HTTPServer.ListenAddr).ListenAndServe())
}

View File

@@ -1,5 +0,0 @@
package gtun
import (
_ "github.com/ICKelin/gtun/gtun/proxy"
)

View File

@@ -1,83 +0,0 @@
package proxy
import (
"fmt"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/utils"
"strings"
"sync/atomic"
)
var (
markID = int32(1)
routeTableID = int32(101)
)
func allocateMarkID() int32 {
return atomic.AddInt32(&markID, 1)
}
func allocateRouteTableID() int32 {
return atomic.AddInt32(&routeTableID, 1)
}
func initRedirect(proto, region, redirectPort string) {
setName := ipsetNamePrefix + region
out, err := utils.ExecCmd("ipset", []string{"create", setName, "hash:net"})
if err != nil {
logs.Warn("create ipset fail: %v %s", err, out)
}
out, err = utils.ExecCmd("ipset", []string{"-F", setName})
if err != nil {
logs.Warn("flush ipset fail: %v %s", err, out)
}
markID := allocateMarkID()
routeTable := allocateRouteTableID()
args := fmt.Sprintf("-t mangle -D PREROUTING -p %s -m set --match-set %s dst -j TPROXY --tproxy-mark %d/%d --on-port %s", proto, setName, markID, markID, redirectPort)
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("-t mangle -A PREROUTING -p %s -m set --match-set %s dst -j TPROXY --tproxy-mark %d/%d --on-port %s", proto, setName, markID, markID, redirectPort)
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("-t mangle -D OUTPUT -p %s -m set --match-set %s dst -j MARK --set-mark %d", proto, setName, markID)
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("-t mangle -A OUTPUT -p %s -m set --match-set %s dst -j MARK --set-mark %d", proto, setName, markID)
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("rule del fwmark %d lookup %d", markID, routeTable)
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("rule add fwmark %d lookup %d", markID, routeTable)
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("ro del local default dev lo table %d", routeTable)
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
args = fmt.Sprintf("ro add local default dev lo table %d", routeTable)
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
if err != nil {
logs.Warn("%s %s %s", args, err, out)
}
}

View File

@@ -1,61 +0,0 @@
package proxy
import (
"bytes"
"net"
"testing"
"time"
)
type dummyConn struct {
readBuf bytes.Buffer
writeBuf bytes.Buffer
}
func (d *dummyConn) Read(b []byte) (n int, err error) {
n = copy(b, d.readBuf.Bytes())
return n, nil
}
func (d *dummyConn) Write(b []byte) (n int, err error) {
return d.writeBuf.Write(b)
}
func (d *dummyConn) Close() error {
d.readBuf.Reset()
d.writeBuf.Reset()
return nil
}
func (d *dummyConn) LocalAddr() net.Addr {
return nil
}
func (d *dummyConn) RemoteAddr() net.Addr {
return nil
}
func (d *dummyConn) SetDeadline(t time.Time) error {
return nil
}
func (d *dummyConn) SetReadDeadline(t time.Time) error {
return nil
}
func (d *dummyConn) SetWriteDeadline(t time.Time) error {
return nil
}
func TestTProxyTCPDoProxy(t *testing.T) {
p := NewTProxyTCP()
cfg := `{}`
err := p.Setup([]byte(cfg))
if err != nil {
t.Error(err)
return
}
conn := &dummyConn{}
p.(*TProxyTCP).doProxy(conn)
}

View File

@@ -1,146 +0,0 @@
package proxy
import (
"encoding/binary"
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/gtun/route"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/proto"
"github.com/ICKelin/gtun/internal/utils"
"github.com/ICKelin/optw/transport"
"io"
"time"
)
func init() {
Register("tun_proxy", NewTunProxy)
}
type TunProxyConfig struct {
Region string `json:"region"`
MTU int `json:"mtu"`
WriteTimeout int `json:"write_timeout"`
ReadTimeout int `json:"read_timeout"`
}
type TunProxy struct {
config TunProxyConfig
dev *utils.Interface
}
func NewTunProxy() Proxy {
return &TunProxy{}
}
func (p *TunProxy) Name() string {
return "tun_proxy"
}
func (p *TunProxy) Setup(cfg json.RawMessage) error {
var config = TunProxyConfig{}
err := json.Unmarshal(cfg, &config)
if err != nil {
return err
}
if config.MTU <= 0 {
return fmt.Errorf("%s invalid mtu", p.Name())
}
dev, err := utils.NewInterface()
if err != nil {
return err
}
err = dev.SetMTU(config.MTU)
if err != nil {
return err
}
err = dev.Up()
if err != nil {
return err
}
p.config = config
p.dev = dev
return nil
}
func (p *TunProxy) ListenAndServe() error {
// tun proxy use only one stream for long live connection
var nextHopStream transport.Stream
var nextHopConn *route.HopInfo
for {
buf, err := p.dev.Read()
if err != nil {
return err
}
if nextHopConn == nil || nextHopConn.IsClosed() {
nextHopConn = route.GetRouteManager().Route(p.config.Region, "")
if nextHopConn == nil {
logs.Warn("route to next hop fail")
continue
}
nextHopStream, err = nextHopConn.OpenStream()
if err != nil {
logs.Warn("open stream fail: %v", err)
continue
}
// encode proxy protocol
bytes := proto.EncodeProxyProtocol("tun_proxy", "", "0", "", "0")
_ = nextHopStream.SetWriteDeadline(time.Now().Add(time.Duration(p.config.WriteTimeout)))
_, err = nextHopStream.Write(bytes)
_ = nextHopStream.SetWriteDeadline(time.Time{})
go p.readFromRemote(nextHopStream)
}
bytes := proto.EncodeData(buf)
nextHopStream.SetWriteDeadline(time.Now().Add(time.Duration(p.config.WriteTimeout)))
_, err = nextHopStream.Write(bytes)
nextHopStream.SetWriteDeadline(time.Time{})
if err != nil {
nextHopStream.Close()
nextHopConn.Close()
logs.Error("stream write fail: %v", err)
}
}
}
func (p *TunProxy) readFromRemote(stream transport.Stream) {
defer stream.Close()
hdr := make([]byte, 2)
for {
nr, err := stream.Read(hdr)
if err != nil {
if err != io.EOF {
logs.Error("read stream fail %v", err)
}
break
}
if nr != 2 {
logs.Error("invalid bodylen: %d", nr)
continue
}
nlen := binary.BigEndian.Uint16(hdr)
buf := make([]byte, nlen)
stream.SetReadDeadline(time.Now().Add(time.Duration(p.config.ReadTimeout)))
_, err = io.ReadFull(stream, buf)
stream.SetReadDeadline(time.Time{})
if err != nil {
logs.Error("read stream body fail: %v", err)
break
}
_, err = p.dev.Write(buf)
if err != nil {
logs.Warn("write to dev fail: %v", err)
return
}
}
}

View File

@@ -1,130 +0,0 @@
package proxy
import (
"bufio"
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/utils"
"net/http"
"os"
"strings"
"time"
)
var errRegistered = fmt.Errorf("already registered")
var errNotRegister = fmt.Errorf("proxy not register")
var ipsetNamePrefix = "GTUN-"
// Proxy defines Proxies, such as tproxy_tcp, tproxy_udp,ip_tun, ip_wireguard
type Proxy interface {
Name() string
Setup(cfg json.RawMessage) error
ListenAndServe() error
}
var registerProxy = make(map[string]func() Proxy)
func Register(name string, constructor func() Proxy) error {
if _, ok := registerProxy[name]; ok {
return errRegistered
}
registerProxy[name] = constructor
return nil
}
func Setup(region, ruleFile string, proxyConfigs map[string]string) error {
for name, config := range proxyConfigs {
constructor := registerProxy[name]
if constructor == nil {
return errNotRegister
}
p := constructor()
err := p.Setup([]byte(config))
if err != nil {
return err
}
AddFromFile(region, ruleFile)
go p.ListenAndServe()
}
return nil
}
func AddIP(region string, ip string) error {
out, err := utils.ExecCmd("ipset", []string{"add", ipsetNamePrefix + region, ip})
if err != nil {
return fmt.Errorf("add to ipset fail: %v %s", err, out)
}
return nil
}
func DelIP(region, ip string) error {
out, err := utils.ExecCmd("ipset", []string{"del", ipsetNamePrefix + region, ip})
if err != nil {
return fmt.Errorf("del from ipset fail: %v %s", err, out)
}
return nil
}
func AddApp(region, appName string) error {
AddFromFile(region, appName)
return nil
}
func AddFromFile(region, file string) {
ips := loadIPs(file)
for _, ip := range ips {
AddIP(region, ip)
}
}
func loadIPs(file string) []string {
if len(file) <= 0 {
return nil
}
ips := make([]string, 0)
var br *bufio.Reader
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
// load from url
req, err := http.NewRequest("GET", file, nil)
if err != nil {
logs.Warn("load file fail: %v", err)
return nil
}
cli := http.Client{
Timeout: time.Second * 120,
}
resp, err := cli.Do(req)
if err != nil {
logs.Warn("load file fail: %v", err)
return nil
}
defer resp.Body.Close()
br = bufio.NewReader(resp.Body)
} else {
// load from file
fp, err := os.Open(file)
if err != nil {
logs.Warn("open rule file fail: %v", err)
return nil
}
defer fp.Close()
br = bufio.NewReader(fp)
}
for {
line, _, err := br.ReadLine()
if err != nil {
break
}
ips = append(ips, string(line))
}
return ips
}

View File

@@ -1,105 +0,0 @@
package route
import (
"github.com/ICKelin/gtun/internal/logs"
"strings"
"sync"
"github.com/ICKelin/optw/transport"
)
var routeManager = &Manager{
regionHops: make(map[string][]*HopInfo),
raceManager: GetTraceManager(),
}
type Manager struct {
raceManager *TraceManager
regionHopsMu sync.RWMutex
regionHops map[string][]*HopInfo
}
type HopInfo struct {
transport.Conn
}
func GetRouteManager() *Manager {
return routeManager
}
func (routeManager *Manager) Route(region, dip string) *HopInfo {
routeManager.regionHopsMu.RLock()
defer routeManager.regionHopsMu.RUnlock()
regionHops, ok := routeManager.regionHops[region]
if !ok {
return nil
}
if len(regionHops) <= 0 {
return nil
}
bestNode := routeManager.raceManager.GetBestNode(region)
bestIP := strings.Split(bestNode, ":")[0]
for i := 0; i < len(regionHops); i++ {
hop := regionHops[i]
if hop.IsClosed() {
logs.Warn("%s %s is closed", region, hop.RemoteAddr())
continue
}
if len(bestIP) != 0 {
// use only ip address for the same node
// TODO: use scheme://ip:port
hopIP := strings.Split(hop.RemoteAddr().String(), ":")[0]
if bestIP == hopIP {
logs.Debug("best ip match %s", bestIP)
return hop
}
}
}
logs.Warn("use random hop")
hash := 0
for _, c := range dip {
hash += int(c)
}
hop := regionHops[hash%len(regionHops)]
if hop == nil || hop.IsClosed() {
return nil
}
return hop
}
func (routeManager *Manager) AddRoute(region string, hop *HopInfo) {
routeManager.regionHopsMu.Lock()
defer routeManager.regionHopsMu.Unlock()
regionHops := routeManager.regionHops[region]
if regionHops == nil {
regionHops = make([]*HopInfo, 0)
}
regionHops = append(regionHops, hop)
routeManager.regionHops[region] = regionHops
}
func (routeManager *Manager) DeleteRoute(region string, hop *HopInfo) {
routeManager.regionHopsMu.Lock()
defer routeManager.regionHopsMu.Unlock()
regionHops := routeManager.regionHops[region]
if regionHops == nil {
return
}
hops := make([]*HopInfo, 0, len(regionHops))
for _, s := range regionHops {
if s.RemoteAddr().String() == hop.RemoteAddr().String() {
continue
}
hops = append(hops, s)
}
routeManager.regionHops[region] = hops
}

View File

@@ -1,191 +0,0 @@
package route
import (
"github.com/ICKelin/optw/transport"
"github.com/agiledragon/gomonkey/v2"
. "github.com/smartystreets/goconvey/convey"
"net"
"testing"
)
func TestManager_AddRoute(t *testing.T) {
Convey("Test add route", t, func() {
Convey("add first hop for region", func() {
GetRouteManager().AddRoute("test_region1", &HopInfo{})
So(len(GetRouteManager().regionHops), ShouldEqual, 1)
So(len(GetRouteManager().regionHops["test_region1"]), ShouldEqual, 1)
})
Convey("add second hop", func() {
GetRouteManager().AddRoute("test_region1", &HopInfo{})
So(len(GetRouteManager().regionHops), ShouldEqual, 1)
So(len(GetRouteManager().regionHops["test_region1"]), ShouldEqual, 2)
})
})
}
func TestManager_DeleteRoute(t *testing.T) {
Convey("Test delete route", t, func() {
Convey("delete un exist region", func() {
GetRouteManager().DeleteRoute("not-found", nil)
})
Convey("delete exist region with empty hops", func() {
GetRouteManager().regionHops["region1"] = make([]*HopInfo, 0)
GetRouteManager().DeleteRoute("region1", nil)
})
Convey("delete exist region with hops", func() {
newHopInfo := &HopInfo{Conn: &mockConn{}}
gomonkey.ApplyMethod(newHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
return &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 10000,
Zone: "",
}
})
GetRouteManager().AddRoute("region1", newHopInfo)
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
GetRouteManager().DeleteRoute("region1", newHopInfo)
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 0)
})
Convey("delete not exist hops", func() {
newHopInfo := &HopInfo{Conn: &mockConn{}}
i := 0
gomonkey.ApplyMethod(newHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
i += 1
return &net.TCPAddr{
IP: net.IPv4(127, 0, byte(i), 1),
Port: 10000,
Zone: "",
}
})
deleteHopInfo := &HopInfo{Conn: &mockConn{}}
gomonkey.ApplyMethod(deleteHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
i += 1
return &net.TCPAddr{
IP: net.IPv4(127, 0, byte(i), 1),
Port: 10000,
Zone: "",
}
})
GetRouteManager().AddRoute("region1", newHopInfo)
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
GetRouteManager().DeleteRoute("region1", deleteHopInfo)
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
})
})
}
func TestManager_Route(t *testing.T) {
Convey("Test route", t, func() {
Convey("nil next hop region", func() {
hop := GetRouteManager().Route("not-found", "")
So(hop, ShouldBeNil)
GetRouteManager().regionHops["region1"] = make([]*HopInfo, 0)
hop = GetRouteManager().Route("region1", "")
So(hop, ShouldBeNil)
})
Convey("next hop is closed", func() {
hop := &HopInfo{Conn: &mockConn{}}
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
return true
})
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
return &net.TCPAddr{
IP: net.IPv4(127, 0, byte(0), 1),
Port: 10000,
Zone: "",
}
})
GetRouteManager().AddRoute("region1", hop)
routeHop := GetRouteManager().Route("region1", "")
So(routeHop, ShouldBeNil)
})
Convey("best ip not match", func() {
hop := &HopInfo{Conn: &mockConn{}}
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
return false
})
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
return &net.TCPAddr{
IP: net.IPv4(127, 0, byte(0), 1),
Port: 10000,
Zone: "",
}
})
gomonkey.ApplyMethod(GetRouteManager().raceManager, "GetBestNode", func(manager *TraceManager) string {
return "192.168.1.1:9000"
})
GetRouteManager().AddRoute("region2", hop)
routeHop := GetRouteManager().Route("region2", "")
// use random
So(routeHop, ShouldNotBeNil)
So(routeHop.RemoteAddr().String(), ShouldEqual, "127.0.0.1:10000")
})
Convey("best ip match", func() {
hop := &HopInfo{Conn: &mockConn{}}
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
return false
})
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
return &net.TCPAddr{
IP: net.IPv4(127, 0, byte(0), 1),
Port: 10000,
Zone: "",
}
})
gomonkey.ApplyMethod(GetRouteManager().raceManager, "GetBestNode", func(manager *TraceManager) string {
return "127.0.0.1:10000"
})
GetRouteManager().AddRoute("region3", hop)
routeHop := GetRouteManager().Route("region3", "")
So(routeHop, ShouldNotBeNil)
So(routeHop.RemoteAddr().String(), ShouldEqual, "127.0.0.1:10000")
})
Convey("random next hop", func() {})
})
}
type mockConn struct{}
func (m *mockConn) OpenStream() (transport.Stream, error) {
//TODO implement me
panic("implement me")
}
func (m *mockConn) AcceptStream() (transport.Stream, error) {
//TODO implement me
panic("implement me")
}
func (m *mockConn) Close() {
//TODO implement me
panic("implement me")
}
func (m *mockConn) IsClosed() bool {
//TODO implement me
panic("implement me")
}
func (m *mockConn) RemoteAddr() net.Addr {
//TODO implement me
panic("implement me")
}
var _ transport.Conn = &mockConn{}

View File

@@ -1,56 +0,0 @@
package gtund
import (
"flag"
"fmt"
"net/http"
_ "net/http/pprof"
"github.com/ICKelin/optw/transport/transport_api"
"github.com/ICKelin/gtun/internal/logs"
)
var version = ""
func init() {
go http.ListenAndServe(":6060", nil)
}
func Main() {
flgVersion := flag.Bool("v", false, "print version")
flgConf := flag.String("c", "", "config file")
flag.Parse()
if *flgVersion {
fmt.Println(version)
return
}
conf, err := ParseConfig(*flgConf)
if err != nil {
fmt.Printf("parse config file fail: %s %v\n", *flgConf, err)
return
}
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
logs.Debug("config: %s", conf.String())
if conf.Trace != "" {
go NewTraceServer(conf.Trace).ListenAndServe()
}
for _, cfg := range conf.ServerConfig {
listener, err := transport_api.NewListen(cfg.Scheme, cfg.Listen, cfg.ListenerConfig)
if err != nil {
logs.Error("new listener fail: %v", err)
return
}
defer listener.Close()
s := NewServer(listener)
go s.Run()
}
select {}
}

View File

@@ -1,9 +0,0 @@
echo "add no proxy address"
ipset create GTUN-NOPROXY hash:net
iptables -t mangle -I PREROUTING -m set --match-set GTUN-NOPROXY dst -j ACCEPT
iptables -t mangle -I OUTPUT -m set --match-set GTUN-NOPROXY dst -j ACCEPT
echo "start gtun"
nohup ./gtun-linux-amd64 -c gtun.yaml &
echo "start success."

72967
scripts/dnsmasq/cn.conf Normal file

File diff suppressed because it is too large Load Diff

72967
scripts/dnsmasq/cn_set.conf Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,679 @@
# Configuration file for dnsmasq.
#
# Format is one option per line, legal options are the same
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.
#port=5353
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
# Never forward plain names (without a dot or domain part)
#domain-needed
# Never forward addresses in the non-routed address spaces.
#bogus-priv
# Uncomment these to enable DNSSEC validation and caching:
# (Requires dnsmasq to be built with DNSSEC option.)
#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
#dnssec
# Replies which are not DNSSEC signed may be legitimate, because the domain
# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
# check that an unsigned reply is OK, by finding a secure proof that a DS
# record somewhere between the root and the domain does not exist.
# The cost of setting this is that even queries in unsigned domains will need
# one or more extra DNS queries to verify.
#dnssec-check-unsigned
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos, SIP, XMMP or Google-talk.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k
# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
resolv-file=/etc/dnsmasq.resolv
# By default, dnsmasq will send queries to any of the upstream
# servers it knows about and tries to favour servers to are known
# to be up. Uncommenting this forces dnsmasq to try each query
# with each server strictly in the order they appear in
# /etc/resolv.conf
#strict-order
# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers from this file instead (see below), then
# uncomment this.
#no-resolv
# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
# files for changes and re-read them then uncomment this.
#no-poll
# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver 10.1.2.3
#server=/3.168.192.in-addr.arpa/10.1.2.3
# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.
#local=/localnet/
# Add domains which you want to force to an IP address here.
# The example below send any host in double-click.net to a local
# web-server.
#address=/double-click.net/127.0.0.1
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# Add the IPs of all queries to yahoo.com, google.com, and their
# subdomains to the vpn and search ipsets:
#ipset=/yahoo.com/google.com/vpn,search
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# server=10.1.2.3@eth1
# and this sets the source (ie local) address used to talk to
# 10.1.2.3 to 192.168.1.1 port 55 (there must be an interface with that
# IP on the machine, obviously).
# server=10.1.2.3@192.168.1.1#55
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
#user=
#group=
# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
#interface=enp4s0
# Or you can specify which interface _not_ to listen on
#except-interface=
# Or which to listen on by address (remember to include 127.0.0.1 if
# you use this.)
#listen-address=192.168.2.131
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
#no-hosts
# or if you want it to read another file, as well as /etc/hosts, use
# this.
#addn-hosts=/etc/banner_add_hosts
# Set this (and domain: see below) if you want to have a domain
# automatically added to simple names in a hosts-file.
#expand-hosts
# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
# as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Set a different domain for a particular subnet
#domain=wireless.thekelleys.org.uk,192.168.2.0/24
# Same idea, but range rather then subnet
#domain=reserved.thekelleys.org.uk,192.68.3.100,192.168.3.200
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
# This is an example of a DHCP range which sets a tag, so that
# some DHCP options may be set only for this network.
#dhcp-range=set:red,192.168.0.50,192.168.0.150
# Use this DHCP range only when the tag "green" is set.
#dhcp-range=tag:green,192.168.0.50,192.168.0.150,12h
# Specify a subnet which can't be used for dynamic address allocation,
# is available for hosts with matching --dhcp-host lines. Note that
# dhcp-host declarations will be ignored unless there is a dhcp-range
# of some type for the subnet in question.
# In this case the netmask is implied (it comes from the network
# configuration on the machine running dnsmasq) it is possible to give
# an explicit netmask instead.
#dhcp-range=192.168.0.0,static
# Enable DHCPv6. Note that the prefix-length does not need to be specified
# and defaults to 64 if missing/
#dhcp-range=1234::2, 1234::500, 64, 12h
# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only
# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC algorithm.
#dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet.
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
#dhcp-range=1234::, ra-only, 48h
# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
# so that clients can use SLAAC addresses as well as DHCP ones.
#dhcp-range=1234::2, 1234::500, slaac
# Do Router Advertisements and stateless DHCP for this subnet. Clients will
# not get addresses from DHCP, but they will get other configuration information.
# They will use SLAAC for addresses.
#dhcp-range=1234::, ra-stateless
# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
# from DHCPv4 leases.
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overridden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
#enable-ra
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissible to give name, address and MAC in any
# order.
# Always allocate the host with Ethernet address 11:22:33:44:55:66
# The IP address 192.168.0.60
#dhcp-host=11:22:33:44:55:66,192.168.0.60
# Always set the name of the host with hardware address
# 11:22:33:44:55:66 to be "fred"
#dhcp-host=11:22:33:44:55:66,fred
# Always give the host with Ethernet address 11:22:33:44:55:66
# the name fred and IP address 192.168.0.60 and lease time 45 minutes
#dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
# Give a host with Ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
# that these two Ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.
#dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.60
# Give the machine which says its name is "bert" IP address
# 192.168.0.70 and an infinite lease
#dhcp-host=bert,192.168.0.70,infinite
# Always give the host with client identifier 01:02:02:04
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the InfiniBand interface with hardware address
# 80:00:00:48:fe:80:00:00:00:00:00:00:f4:52:14:03:00:28:05:81 the
# ip address 192.168.0.61. The client id is derived from the prefix
# ff:00:00:00:00:00:02:00:00:02:c9:00 and the last 8 pairs of
# hex digits of the hardware address.
#dhcp-host=id:ff:00:00:00:00:00:02:00:00:02:c9:00:f4:52:14:03:00:28:05:81,192.168.0.61
# Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60
# Enable the address given for "judge" in /etc/hosts
# to be given to a machine presenting the name "judge" when
# it asks for a DHCP lease.
#dhcp-host=judge
# Never offer DHCP service to a machine whose Ethernet
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with Ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with Ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,set:red
# Send extra options which are tagged as "red" to
# any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also that the [] around the IPv6 address are obligatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=tag:!known
# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=set:red,Linux
# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"
#dhcp-userclass=set:red,accounts
# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.
#dhcp-mac=set:red,00:60:8C:*:*:*
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
# MAC-address/host mappings there for other purposes.
#read-ethers
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
#dhcp-option=3,1.2.3.4
# Do the same thing, but using the option name
#dhcp-option=option:router,1.2.3.4
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
# Send DHCPv6 option. Note [] around IPv6 addresses.
#dhcp-option=option6:dns-server,[1234::77],[1234::88]
# Send DHCPv6 option for namservers as the machine running
# dnsmasq and another.
#dhcp-option=option6:dns-server,[::],[1234::88]
# Ask client to poll for option changes every six hours. (RFC4242)
#dhcp-option=option6:information-refresh-time,6h
# Set option 58 client renewal time (T1). Defaults to half of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T1,1m
# Set option 59 rebinding time (T2). Defaults to 7/8 of the
# lease time if not specified. (RFC2132)
#dhcp-option=option:T2,2m
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
# Set the NIS domain name to "welly"
#dhcp-option=40,welly
# Set the default time-to-live to 50
#dhcp-option=23,50
# Set the "all subnets are local" flag
#dhcp-option=27,1
# Send the etherboot magic flag and then etherboot options (a string).
#dhcp-option=128,e4:45:74:68:00:00
#dhcp-option=129,NIC=eepro100
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
# Note that the tag: part must precede the option: part.
#dhcp-option = tag:red, option:ntp-server, 192.168.1.1
# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment some or all of them if you use
# Windows clients and Samba.
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type
# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
#dhcp-option=252,"\n"
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
# Send RFC-3442 classless static routes (note the netmask encoding)
#dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to 0.0.0.0 for PXEClients.
#dhcp-option=vendor:PXEClient,1,0.0.0.0
# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
# value as a four-byte integer - that's what microsoft wants. See
# http://technet2.microsoft.com/WindowsServer/en/library/a70f1bb7-d2d4-49f0-96d6-4b7414ecfaae1033.mspx?mfr=true
#dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot"
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
# Configuration file name
#dhcp-option-force=209,configs/common
# Path prefix
#dhcp-option-force=210,/tftpboot/pxelinux/files/
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for netboot/PXE. You will only need
# this if you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built-in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# Boot for iPXE. The idea is to send two different
# filenames, the first loads iPXE, and the second tells iPXE what to
# load. The dhcp-match sets the ipxe tag for requests from iPXE.
#dhcp-boot=undionly.kpxe
#dhcp-match=set:ipxe,175 # iPXE sends a 175 option.
#dhcp-boot=tag:ipxe,http://boot.ipxe.org/demo/boot.php
# Encapsulated options for iPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 189, 1b # BIOS drive code
#dhcp-option=encap:175, 190, user # iSCSI username
#dhcp-option=encap:175, 191, pass # iSCSI password
# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578)
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
#pxe-prompt="What system shall I netboot?"
# or with timeout before first available action is taken:
#pxe-prompt="Press F8 for menu.", 60
# Available boot services. for PXE.
#pxe-service=x86PC, "Boot from local disk"
# Loads <tftp-root>/pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux
# Loads <tftp-root>/pxelinux.0 from TFTP server at 1.2.3.4.
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1
# Use bootserver at a known IP address.
#pxe-service=x86PC, "Install windows from RIS server", 1, 1.2.3.4
# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Do not abort if the tftp-root is unavailable
#tftp-no-fail
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.
#tftp-no-blocksize
# Set the boot file name only when the "red" tag is set.
#dhcp-boot=tag:red,pxelinux.red-net
# An example of dhcp-boot with an external TFTP server: the name and IP
# address of the server are given after the filename.
# Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fashion. This facility can be used to
# load balance the tftp load among a set of servers.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.
#dhcp-leasefile=/var/lib/misc/dnsmasq.leases
# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slightest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/files/auth.html
#dhcp-authoritative
# Set the DHCP server to enable DHCPv4 Rapid Commit Option per RFC 4039.
# In this mode it will respond to a DHCPDISCOVER message including a Rapid Commit
# option with a DHCPACK including a Rapid Commit option and fully committed address
# and configuration information. This must only be enabled if either the server is
# the only server for the subnet, or multiple servers are present and they each
# commit a binding for all clients.
#dhcp-rapid-commit
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.
#dhcp-script=/bin/echo
# Set the cachesize here.
#cache-size=150
# If you want to disable negative caching, uncomment this.
#no-negcache
# Normally responses which come from /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# seconds) here.
#local-ttl=
# If you want dnsmasq to detect attempts by Verisign to send queries
# to unregistered .com and .net hosts to its sitefinder service and
# have dnsmasq instead return the correct NXDOMAIN response, uncomment
# this line. You can add similar lines to do the same for other
# registries which have implemented wildcard A records.
#bogus-nxdomain=64.94.110.11
# If you want to fix up DNS results from upstream servers, use the
# alias option. This only works for IPv4.
# This alias makes a result of 1.2.3.4 appear as 5.6.7.8
#alias=1.2.3.4,5.6.7.8
# and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0
# and this maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
#alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 389
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 389 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,"v=spf1 a -all"
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand
#cname=bertand,bert
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries
# Log lots of extra information about DHCP transactions.
#log-dhcp
# Include another lot of configuration options.
#conf-file=/etc/dnsmasq.more.conf
#conf-dir=/etc/dnsmasq.d
# Include all the files in a directory except those ending in .bak
#conf-dir=/etc/dnsmasq.d,.bak
# Include all files in a directory which end in .conf
#conf-dir=/etc/dnsmasq.d/,*.conf
# If a DHCP client claims that its name is "wpad", ignore that.
# This fixes a security hole. see CERT Vulnerability VU#598349
#dhcp-name-match=set:wpad-ignore,wpad
#dhcp-ignore-names=tag:wpad-ignore

View File

@@ -0,0 +1 @@
nameserver 8.8.8.8

7
scripts/install_gtun.sh Executable file
View File

@@ -0,0 +1,7 @@
systemctl stop gtun
GTUN_DIR="/opt/apps/gtun"
mkdir -p $GTUN_DIR/logs
cp -r . $GTUN_DIR
cp etc/gtun.service /lib/systemd/system/
systemctl daemon-reload
systemctl start gtun

7
scripts/install_gtund.sh Executable file
View File

@@ -0,0 +1,7 @@
systemctl stop gtund
GTUND_DIR="/opt/apps/gtund"
mkdir -p $GTUND_DIR/logs
cp -r . $GTUND_DIR
cp etc/gtund.service /lib/systemd/system/
systemctl daemon-reload
systemctl start gtund

17
scripts/noproxy.txt Normal file
View File

@@ -0,0 +1,17 @@
114.114.114.114
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/24
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.255.255.255/32

65
scripts/redirect_all.sh Normal file
View File

@@ -0,0 +1,65 @@
setname=GTUN_ALL
noproxy_set=NOPROXY
redirect_port=8524
clear_noproxy() {
iptables -t mangle -D PREROUTING -m set --match-set $noproxy_set dst -j ACCEPT
iptables -t mangle -D OUTPUT -m set --match-set $noproxy_set dst -j ACCEPT
ipset destroy $noproxy_set >/dev/null
}
add_noproxy() {
ipset create $noproxy_set hash:net
cat noproxy.txt | while read line
do
echo "no proxy for" $line
ipset add $noproxy_set $line
done
iptables -t mangle -A PREROUTING -m set --match-set $noproxy_set dst -j ACCEPT
iptables -t mangle -A OUTPUT -m set --match-set $noproxy_set dst -j ACCEPT
}
clear_proxy() {
ip ro del local default dev lo table 100
ip rule del fwmark 1 lookup 100
iptables -t mangle -D PREROUTING -p tcp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -D PREROUTING -p udp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -D OUTPUT -m set --match-set $setname dst -j MARK --set-mark 1 >/dev/null
ipset destroy $setname >/dev/null
}
add_proxy() {
ipset create $setname hash:net
echo "proxy for 0.0.0.0/1"
echo "proxy for 128.0.0.0/1"
ipset add $setname 0.0.0.0/1
ipset add $setname 128.0.0.0/1
iptables -t mangle -A PREROUTING -p tcp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -A PREROUTING -p udp -m set --match-set $setname dst -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
iptables -t mangle -A OUTPUT -m set --match-set $setname dst -j MARK --set-mark 1
# redirect dns query
# iptables -t mangle -A PREROUTING -p udp --dport 53 -j TPROXY --tproxy-mark 1/1 --on-port $redirect_port
# iptables -t mangle -A OUTPUT -p udp --dport 53 -j MARK --set-mark 1
ip rule add fwmark 1 lookup 100
ip ro add local default dev lo table 100
}
sep="============================================"
echo "gtun accelerator redirect configure"
clear_noproxy
clear_proxy
echo "Adding noproxy cidrs"
add_noproxy
echo "Done."
echo $sep
echo "Adding proxy cidrs"
add_proxy
echo "Done."
echo $sep

View File

@@ -0,0 +1,16 @@
config_dnsmasq() {
echo "configuring dnsmasq service"
cp dnsmasq/dnsmasq.conf /etc/dnsmasq.conf
cp dnsmasq/dnsmasq.resolv /etc/dnsmasq.resolv
echo "configuring dnsmasq cn domain list"
cp dnsmasq/cn.conf /etc/dnsmasq.d/
cp dnsmasq/cn_set.conf /etc/dnsmasq.d/
systemctl restart dnsmasq
}
./redirect_all.sh
echo "Configuring dnsmasq"
config_dnsmasq
echo "Done."
echo $sep

65
src/gtun/config/config.go Normal file
View File

@@ -0,0 +1,65 @@
package config
import (
"github.com/ICKelin/gtun/src/internal/signature"
"gopkg.in/yaml.v2"
"os"
)
var gConfig *Config
var signatureKey = os.Getenv("GTUN_SIGNATURE")
type Config struct {
AccessToken string `yaml:"access_token"`
Accelerator map[string]Accelerator `yaml:"accelerator"`
Log Log `yaml:"log"`
}
type RouteConfig struct {
Scheme string `yaml:"scheme" json:"scheme"`
Server string `yaml:"server" json:"server"`
Trace string `yaml:"trace" json:"trace"`
AuthKey string `yaml:"auth_key" json:"auth_key"`
}
type Accelerator struct {
Region string `json:"region"`
GeoSite []string `json:"geo_site"`
GeoIP []string `json:"geo_ip"`
Routes []*RouteConfig `json:"routes"`
Proxy map[string]string `json:"proxy"`
}
type Log struct {
Days int64 `yaml:"days"`
Level string `yaml:"level"`
Path string `yaml:"path"`
}
func Parse(path string) (*Config, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return ParseBuffer(content)
}
func ParseBuffer(content []byte) (*Config, error) {
configContent, err := signature.UnSign(content)
if err != nil {
return nil, err
}
conf := Config{}
err = yaml.Unmarshal([]byte(os.ExpandEnv(string(configContent))), &conf)
if err != nil {
return nil, err
}
gConfig = &conf
return &conf, err
}
func Default() *Config {
return gConfig
}

View File

@@ -0,0 +1,69 @@
package config
import (
"github.com/ICKelin/gtun/src/internal/signature"
"github.com/smartystreets/goconvey/convey"
"os"
"testing"
)
var testConfig = `
access_token: "${ACCESS_TOKEN}"
accelerator:
HK:
routes:
- scheme: "kcp"
server: "${SERVER_IP}:3002"
trace: "${SERVER_IP}:3003"
- scheme: "mux"
server: "${SERVER_IP}:3002"
trace: "${SERVER_IP}:3003"
proxy:
tproxy_tcp: |
{
"read_timeout": 30,
"write_timeout": 30,
"listen_addr": ":8524"
}
tproxy_udp: |
{
"read_timeout": 30,
"write_timeout": 30,
"session_timeout": 30,
"listen_addr": ":8524"
}
log:
days: 5
level: debug
path: /opt/apps/gtun/logs/gtun.log
`
func TestConfig(t *testing.T) {
convey.Convey("test config", t, func() {
convey.Convey("test with env var", func() {
os.Setenv("SERVER_IP", "127.0.0.1")
os.Setenv("ACCESS_TOKEN", "ICKelin:free")
cfg, err := ParseBuffer([]byte(testConfig))
convey.So(err, convey.ShouldBeNil)
convey.So(cfg.Accelerator["HK"].Routes[0].Server, convey.ShouldEqual, "127.0.0.1:3002")
convey.So(cfg.AccessToken, convey.ShouldEqual, "ICKelin:free")
})
convey.Convey("test signature", func() {
convey.Convey("test config without signature", func() {
signature.SetSignature("sig")
_, err := ParseBuffer([]byte(testConfig))
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("test config with signature", func() {
signature.SetSignature("sig")
buf, err := signature.Sign([]byte(testConfig))
convey.So(err, convey.ShouldBeNil)
_, err = ParseBuffer(buf)
convey.So(err, convey.ShouldBeNil)
})
})
})
}

50
src/gtun/main.go Normal file
View File

@@ -0,0 +1,50 @@
package main
import (
"flag"
"fmt"
"github.com/ICKelin/gtun/src/gtun/config"
"github.com/ICKelin/gtun/src/gtun/proxy"
"github.com/ICKelin/gtun/src/gtun/route"
"github.com/ICKelin/gtun/src/internal/logs"
)
var logo = `
====================================
██████ ████████ ██ ██ ███ ██
██ ██ ██ ██ ████ ██
██ ███ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██████ ██ ████
====================================
https://github.com/ICKelin/gtun
`
func main() {
flgConf := flag.String("c", "", "config file")
flag.Parse()
conf, err := config.Parse(*flgConf)
if err != nil {
fmt.Printf("load config fail: %v\n", err)
return
}
fmt.Println(logo)
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
logs.Info("%s", logo)
for region, cfg := range conf.Accelerator {
err := route.Setup(region, cfg.Routes)
if err != nil {
panic(err)
}
err = proxy.Serve(region, cfg.Proxy)
if err != nil {
panic(err)
}
}
route.Run()
// TODO: watch for config file changes
select {}
}

54
src/gtun/proxy/proxy.go Normal file
View File

@@ -0,0 +1,54 @@
package proxy
import (
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/src/internal/logs"
)
var errRegistered = fmt.Errorf("already registered")
var errNotRegister = fmt.Errorf("proxy not register")
// Proxy defines Proxies, such as tproxy_tcp, tproxy_udp,ip_tun, ip_wireguard
type Proxy interface {
Name() string
Setup(region string, cfg json.RawMessage) error
ListenAndServe() error
}
var registerProxy = make(map[string]func() Proxy)
func Register(name string, constructor func() Proxy) error {
if _, ok := registerProxy[name]; ok {
return errRegistered
}
registerProxy[name] = constructor
return nil
}
func Serve(region string, proxyConfig map[string]string) error {
logs.Debug("region %s proxy config %s", region, proxyConfig)
err := setup(region, proxyConfig)
if err != nil {
fmt.Printf("region[%s] setup proxy fail: %v\n", region, err)
return err
}
return nil
}
func setup(region string, proxyConfigs map[string]string) error {
for name, config := range proxyConfigs {
constructor := registerProxy[name]
if constructor == nil {
return errNotRegister
}
p := constructor()
err := p.Setup(region, json.RawMessage(config))
if err != nil {
return err
}
go p.ListenAndServe()
}
return nil
}

View File

@@ -2,14 +2,11 @@ package proxy
import (
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/gtun/route"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/proto"
"github.com/ICKelin/gtun/internal/utils"
"github.com/ICKelin/gtun/src/gtun/route"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/gtun/src/internal/proto"
"io"
"net"
"strings"
"sync"
"syscall"
"time"
@@ -28,8 +25,6 @@ type TProxyTCPConfig struct {
ReadTimeout int `json:"read_timeout"`
WriteTimeout int `json:"write_timeout"`
ListenAddr string `json:"listen_addr"`
RateLimit int `json:"rate_limit"`
Region string `json:"region"`
}
type TProxyTCP struct {
@@ -46,8 +41,6 @@ type TProxyTCP struct {
mempool sync.Pool
ratelimit *utils.RateLimit
routeManager *route.Manager
}
@@ -59,22 +52,23 @@ func (p *TProxyTCP) Name() string {
return "tproxy_tcp"
}
func (p *TProxyTCP) Setup(cfgContent json.RawMessage) error {
func (p *TProxyTCP) Setup(region string, cfgContent json.RawMessage) error {
var cfg = TProxyTCPConfig{}
err := json.Unmarshal(cfgContent, &cfg)
if err != nil {
return nil
}
err = p.initConfig(cfg)
logs.Debug("region[%s] proxy %s config %s", region, p.Name(), string(cfgContent))
err = p.initConfig(region, cfg)
if err != nil {
return err
}
return p.initRedirect()
return nil
}
func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
func (p *TProxyTCP) initConfig(region string, cfg TProxyTCPConfig) error {
tcpReadTimeout := cfg.ReadTimeout
if tcpReadTimeout <= 0 {
tcpReadTimeout = defaultTCPTimeout
@@ -85,10 +79,7 @@ func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
tcpWriteTimeout = defaultTCPTimeout
}
rateLimit := utils.NewRateLimit()
rateLimit.SetRateLimit(int64(cfg.RateLimit * 1024 * 1024))
p.region = cfg.Region
p.region = region
p.listenAddr = cfg.ListenAddr
p.writeTimeout = time.Duration(tcpWriteTimeout) * time.Second
p.readTimeout = time.Duration(tcpReadTimeout) * time.Second
@@ -97,39 +88,32 @@ func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
return make([]byte, 32*1024)
},
}
p.ratelimit = rateLimit
p.routeManager = route.GetRouteManager()
return nil
}
func (p *TProxyTCP) initRedirect() error {
ipPort := strings.Split(p.listenAddr, ":")
if len(ipPort) != 2 {
return fmt.Errorf("invalid listen addr")
}
initRedirect("tcp", p.region, ipPort[1])
return nil
}
func (p *TProxyTCP) ListenAndServe() error {
listener, err := net.Listen("tcp", p.listenAddr)
if err != nil {
logs.Error("%v", err)
return err
}
// set socket with ip transparent option
file, err := listener.(*net.TCPListener).File()
if err != nil {
logs.Error("%v", err)
return err
}
defer file.Close()
err = syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
if err != nil {
logs.Error("%v", err)
return err
}
logs.Info("region[%s] %s listen %s", p.region, p.Name(), p.listenAddr)
for {
conn, err := listener.Accept()
if err != nil {
@@ -185,6 +169,8 @@ func (p *TProxyTCP) doProxy(conn net.Conn) {
io.CopyBuffer(stream, conn, buf)
}()
defer stream.Close()
defer conn.Close()
obj := p.mempool.Get()
defer p.mempool.Put(obj)
buf := obj.([]byte)

View File

@@ -5,14 +5,13 @@ import (
"encoding/binary"
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/gtun/route"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/proto"
"github.com/ICKelin/gtun/internal/utils"
"github.com/ICKelin/gtun/src/gtun/route"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/gtun/src/internal/proto"
utils2 "github.com/ICKelin/gtun/src/internal/utils"
"github.com/ICKelin/optw/transport"
"io"
"net"
"strings"
"sync"
"syscall"
"time"
@@ -41,8 +40,6 @@ type TProxyUDPConfig struct {
WriteTimeout int `json:"write_timeout"`
SessionTimeout int `json:"session_timeout"`
ListenAddr string `json:"listen_addr"`
RateLimit int `json:"rate_limit"`
Region string `json:"region"`
}
type TProxyUDP struct {
@@ -59,8 +56,6 @@ type TProxyUDP struct {
// the purpose of udpSession is to reuse stream
udpSessions map[string]*udpSession
udpsessLock sync.Mutex
ratelimit *utils.RateLimit
}
func NewTProxyUDP() Proxy {
@@ -71,22 +66,22 @@ func (p *TProxyUDP) Name() string {
return "tproxy_udp"
}
func (p *TProxyUDP) Setup(cfgContent json.RawMessage) error {
func (p *TProxyUDP) Setup(region string, cfgContent json.RawMessage) error {
var cfg = TProxyUDPConfig{}
err := json.Unmarshal(cfgContent, &cfg)
if err != nil {
return nil
}
err = p.initConfig(cfg)
err = p.initConfig(region, cfg)
if err != nil {
return err
}
return p.initRedirect()
return nil
}
func (p *TProxyUDP) initConfig(cfg TProxyUDPConfig) error {
func (p *TProxyUDP) initConfig(region string, cfg TProxyUDPConfig) error {
readTimeout := cfg.ReadTimeout
if readTimeout <= 0 {
readTimeout = defaultUDPTimeout
@@ -102,30 +97,16 @@ func (p *TProxyUDP) initConfig(cfg TProxyUDPConfig) error {
sessionTimeout = defaultUDPSessionTimeout
}
rateLimit := utils.NewRateLimit()
rateLimit.SetRateLimit(int64(cfg.RateLimit * 1024 * 1024))
p.region = cfg.Region
p.region = region
p.listenAddr = cfg.ListenAddr
p.writeTimeout = time.Duration(writeTimeout) * time.Second
p.readTimeout = time.Duration(readTimeout) * time.Second
p.sessionTimeout = time.Duration(sessionTimeout) * time.Second
p.udpSessions = make(map[string]*udpSession)
p.ratelimit = rateLimit
p.routeManager = route.GetRouteManager()
return nil
}
func (p *TProxyUDP) initRedirect() error {
ipPort := strings.Split(p.listenAddr, ":")
if len(ipPort) != 2 {
return fmt.Errorf("invalid listen addr")
}
initRedirect("udp", p.region, ipPort[1])
return nil
}
// ListenAndServe listens an udp port, since that we use tproxy to
// redirect traffic to this listened udp port
// so the socket should set to ip transparent option
@@ -177,6 +158,7 @@ func (p *TProxyUDP) ListenAndServe() error {
}
func (p *TProxyUDP) serve(lconn *net.UDPConn) error {
logs.Info("region[%s] %s listen %s", p.region, p.Name(), p.listenAddr)
go p.recycleSession()
buf := make([]byte, 64*1024)
oob := make([]byte, 1024)
@@ -283,7 +265,7 @@ func (p *TProxyUDP) doProxy(stream transport.Stream, sessionKey string, fromaddr
break
}
err = utils.SendUDPViaRaw(p.rawfd, fromaddr, toaddr, buf)
err = utils2.SendUDPViaRaw(p.rawfd, fromaddr, toaddr, buf)
if err != nil {
logs.Error("send via raw socket fail: %v", err)
}

View File

@@ -2,33 +2,43 @@ package route
import (
"fmt"
"github.com/ICKelin/gtun/src/gtun/config"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/optw/transport/transport_api"
"time"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/optw/transport"
)
type ConnectionConfig struct {
Region string
ServerAddr string
AuthKey string
var cm = &connManager{regionConn: map[string][]*conn{}}
type connManager struct {
regionConn map[string][]*conn
}
type Connection struct {
func (cm *connManager) startConn() {
for _, conns := range cm.regionConn {
for _, conn := range conns {
go conn.connect()
}
}
}
type conn struct {
dialer transport.Dialer
region string
scheme string
serverAddr string
}
func CreateConnection(region, scheme, serverAddr, authKey string) (*Connection, error) {
func newConn(region, scheme, serverAddr, authKey string) (*conn, error) {
dialer, err := transport_api.NewDialer(scheme, serverAddr, authKey)
if err != nil {
return nil, err
}
dialer.SetAccessToken(config.Default().AccessToken)
return &Connection{
return &conn{
dialer: dialer,
region: region,
scheme: scheme,
@@ -36,7 +46,7 @@ func CreateConnection(region, scheme, serverAddr, authKey string) (*Connection,
}, nil
}
func (c *Connection) ConnectNextHop() {
func (c *conn) connect() {
for {
conn, err := c.dialer.Dial()
if err != nil {
@@ -46,23 +56,28 @@ func (c *Connection) ConnectNextHop() {
}
logs.Info("connect to %s success", c.String())
hopConn := &HopInfo{Conn: conn}
// add next hop connection to route
GetRouteManager().AddRoute(c.region, hopConn)
routeEle := &routeItem{
region: c.region,
scheme: c.scheme,
serverAddr: c.serverAddr,
Conn: conn,
}
GetRouteManager().addRoute(c.region, routeEle)
tick := time.NewTicker(time.Second * 1)
for range tick.C {
if hopConn.IsClosed() {
if conn.IsClosed() {
break
}
}
// delete next hop connection to route
GetRouteManager().DeleteRoute(c.region, hopConn)
GetRouteManager().deleteRoute(c.region, routeEle)
logs.Warn("reconnect %s", c.String())
}
}
func (c *Connection) String() string {
func (c *conn) String() string {
return fmt.Sprintf("regions[%s] %s://%s", c.region, c.scheme, c.serverAddr)
}

149
src/gtun/route/route.go Normal file
View File

@@ -0,0 +1,149 @@
package route
import (
"fmt"
"github.com/ICKelin/gtun/src/gtun/config"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/optw/transport"
"sync"
)
var routeManager = &Manager{
tm: tm,
cm: cm,
routeTable: make(map[string][]*routeItem),
}
type routeItem struct {
region string
scheme string
serverAddr string
transport.Conn
}
type Manager struct {
tm *traceManager
cm *connManager
routeTableMu sync.Mutex
routeTable map[string][]*routeItem
}
func GetRouteManager() *Manager {
return routeManager
}
func (routeManager *Manager) Route(region, dip string) transport.Conn {
regionRoutes, ok := routeManager.routeTable[region]
if !ok {
return nil
}
if len(regionRoutes) <= 0 {
return nil
}
bestNode, ok := routeManager.tm.getRegionBestTarget(region)
if ok {
bestAddr := bestNode.serverAddr
for i := 0; i < len(regionRoutes); i++ {
it := regionRoutes[i]
if it.IsClosed() {
logs.Warn("%s %s is closed", region, it.RemoteAddr())
continue
}
if len(bestAddr) != 0 {
// scheme://ip:port match
if bestAddr == it.serverAddr &&
bestNode.scheme == it.scheme {
logs.Debug("region[%s] best node match %s://%s",
region, it.scheme, bestAddr)
return it
}
}
}
} else {
logs.Warn("no best node for region[%s]", region)
}
logs.Warn("region[%s] use random hop", region)
hash := 0
for _, c := range dip {
hash += int(c)
}
hop := regionRoutes[hash%len(regionRoutes)]
if hop == nil || hop.IsClosed() {
return nil
}
return hop
}
func (routeManager *Manager) addRoute(region string, item *routeItem) {
routeManager.routeTableMu.Lock()
defer routeManager.routeTableMu.Unlock()
regionItems := routeManager.routeTable[region]
if regionItems == nil {
regionItems = make([]*routeItem, 0)
}
regionItems = append(regionItems, item)
routeManager.routeTable[region] = regionItems
}
func (routeManager *Manager) deleteRoute(region string, item *routeItem) {
routeManager.routeTableMu.Lock()
defer routeManager.routeTableMu.Unlock()
regionItems := routeManager.routeTable[region]
if regionItems == nil {
return
}
conns := make([]*routeItem, 0, len(regionItems))
for _, it := range regionItems {
if it == item {
continue
}
conns = append(conns, it)
}
routeManager.routeTable[region] = conns
}
func Setup(region string, routes []*config.RouteConfig) error {
for _, cfg := range routes {
conn, err := newConn(region, cfg.Scheme, cfg.Server, cfg.AuthKey)
if err != nil {
fmt.Printf("region[%s] connect to %s://%s fail: %v\n",
region, cfg.Scheme, cfg.Server, cfg.AuthKey)
return err
}
cm.regionConn[region] = append(cm.regionConn[region], conn)
t, ok := tm.regionTrace[region]
if !ok {
logs.Debug("add region[%s] trace", region)
t = newTrace(region)
tm.regionTrace[region] = t
} else {
logs.Debug("region[%s] trace exist", region)
}
t.addTarget(traceTarget{
traceAddr: cfg.Trace,
serverAddr: cfg.Server,
scheme: cfg.Scheme,
})
}
return nil
}
func Run() {
go tm.startTrace()
go cm.startConn()
}

View File

@@ -0,0 +1,107 @@
package route
import (
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/optw/transport"
gomonkey "github.com/agiledragon/gomonkey/v2"
"github.com/smartystreets/goconvey/convey"
"net"
"strings"
"testing"
"time"
)
func TestRoute(t *testing.T) {
convey.Convey("test route", t, func() {
convey.Convey("not exist region", func() {
conn := GetRouteManager().Route("unknown region", "127.0.0.1")
convey.So(conn, convey.ShouldBeNil)
})
convey.Convey("not route to host", func() {
GetRouteManager().routeTable["region1"] = make([]*routeItem, 0)
conn := GetRouteManager().Route("unknown region", "127.0.0.1")
convey.So(conn, convey.ShouldBeNil)
delete(GetRouteManager().routeTable, "region1")
})
convey.Convey("best node", func() {
p := gomonkey.ApplyPrivateMethod(tm, "getRegionBestTarget", func(tm *traceManager, region string) (traceTarget, bool) {
return traceTarget{
traceAddr: "",
serverAddr: "127.0.0.1:5201",
scheme: "kcp",
}, true
})
defer p.Reset()
convey.Convey("best node match", func() {
newRoute := &routeItem{
region: "",
scheme: "kcp",
serverAddr: "127.0.0.1:5201",
Conn: &mockConn{},
}
GetRouteManager().addRoute("region1", newRoute)
defer GetRouteManager().deleteRoute("region1", newRoute)
conn := GetRouteManager().Route("region1", "127.0.0.1:8954")
convey.So(conn, convey.ShouldNotBeNil)
})
convey.Convey("best node not match", func() {
gomonkey.ApplyFunc(logs.Warn, func(f interface{}, v ...interface{}) {
convey.So(strings.Contains(f.(string), "use random hop"), convey.ShouldBeTrue)
})
newRoute := &routeItem{
region: "",
scheme: "kcp",
serverAddr: "127.0.0.1:5202",
Conn: &mockConn{},
}
GetRouteManager().addRoute("region1", newRoute)
defer GetRouteManager().deleteRoute("region1", newRoute)
conn := GetRouteManager().Route("region1", "127.0.0.1:8954")
convey.So(conn, convey.ShouldNotBeNil)
})
})
})
}
type mockConn struct {
}
func (m *mockConn) LocalAddr() net.Addr {
//TODO implement me
panic("implement me")
}
func (m *mockConn) SetDeadline(t time.Time) error {
//TODO implement me
panic("implement me")
}
func (m *mockConn) OpenStream() (transport.Stream, error) {
//TODO implement me
panic("implement me")
}
func (m *mockConn) AcceptStream() (transport.Stream, error) {
//TODO implement me
panic("implement me")
}
func (m *mockConn) Close() {
//TODO implement me
panic("implement me")
}
func (m *mockConn) IsClosed() bool {
return false
}
func (m *mockConn) RemoteAddr() net.Addr {
//TODO implement me
panic("implement me")
}

View File

@@ -2,82 +2,81 @@ package route
import (
"encoding/binary"
"github.com/ICKelin/gtun/src/internal/logs"
"math"
"net"
"sync"
"time"
"github.com/ICKelin/gtun/internal/logs"
)
var traceManager = &TraceManager{
regionTrace: make(map[string]*Trace),
var tm = &traceManager{
regionTrace: make(map[string]*trace),
}
func GetTraceManager() *TraceManager {
return traceManager
type traceManager struct {
regionTrace map[string]*trace
}
// TraceManager manage region trace
type TraceManager struct {
regionTraceMu sync.Mutex
regionTrace map[string]*Trace
}
func (m *TraceManager) RunRace() {
for _, race := range m.regionTrace {
go race.Run()
func (m *traceManager) addTraces(traces map[string]*trace) {
for k, _ := range traces {
m.regionTrace[k] = traces[k]
}
}
// AddRegionTrace adds a trace instance for region
func (m *TraceManager) AddRegionTrace(region string, race *Trace) {
m.regionTraceMu.Lock()
defer m.regionTraceMu.Unlock()
m.regionTrace[region] = race
func (m *traceManager) startTrace() {
for _, trace := range m.regionTrace {
logs.Debug("region[%s] running trace", trace.region)
go trace.runTraceJob()
}
}
// GetBestNode returns the highest score of region target region
func (m *TraceManager) GetBestNode(region string) string {
regionRace := m.regionTrace[region]
if regionRace == nil {
return ""
func (m *traceManager) getRegionBestTarget(region string) (traceTarget, bool) {
regionTrace := m.regionTrace[region]
if regionTrace == nil {
logs.Warn("trace for region[%s] not exist", region)
return traceTarget{}, false
}
return regionRace.GetBestNode()
return regionTrace.getBestNode()
}
// Trace is a region trace instance
type Trace struct {
type traceTarget struct {
traceAddr string
serverAddr string
scheme string
}
type trace struct {
region string
targets []string
targets []traceTarget
targetScoreMu sync.Mutex
targetScore map[string]float64
targetScore map[traceTarget]float64
totalRtt int32
}
// NewTrace return trace instance
func NewTrace(region string, targets []string) *Trace {
return &Trace{
func newTrace(region string) *trace {
return &trace{
region: region,
targets: targets,
targetScoreMu: sync.Mutex{},
targetScore: make(map[string]float64),
targetScore: make(map[traceTarget]float64),
}
}
// Run trace job
func (r *Trace) Run() {
r.trace()
func (t *trace) addTarget(target traceTarget) {
t.targets = append(t.targets, target)
}
func (t *trace) runTraceJob() {
t.trace()
tick := time.NewTicker(time.Second * 120)
for range tick.C {
r.trace()
t.trace()
}
}
func (r *Trace) trace() {
for _, target := range r.targets {
raddr, err := net.ResolveUDPAddr("udp", target)
func (t *trace) trace() {
for i, target := range t.targets {
raddr, err := net.ResolveUDPAddr("udp", target.traceAddr)
if err != nil {
logs.Error("resolve udp addr: %v", err)
continue
@@ -117,21 +116,20 @@ func (r *Trace) trace() {
diff := time.Now().Sub(beg).Milliseconds()
rtt += int(diff)
}
remoteAddr := rconn.RemoteAddr().String()
rconn.Close()
if rtt < 0 {
rtt = math.MaxInt
}
lossRank := r.calcLossScore(loss)
delayRank := r.calcRttScore(rtt)
lossRank := t.calcLossScore(loss)
delayRank := t.calcRttScore(rtt)
score := lossRank + delayRank
logs.Debug("region[%s] %s loss %d rtt %d lossRank %.4f delayRank %.4f score %.4f",
r.region, target, loss, rtt, lossRank, delayRank, score)
r.targetScoreMu.Lock()
r.targetScore[remoteAddr] = score
r.targetScoreMu.Unlock()
t.region, target, loss, rtt, lossRank, delayRank, score)
t.targetScoreMu.Lock()
t.targetScore[t.targets[i]] = score
t.targetScoreMu.Unlock()
}
}
@@ -140,7 +138,7 @@ func (r *Trace) trace() {
// f(p) = 35+(1.25-p)x10 0.75% < p <= 1.25%,
// f(p) = 30+(2.25-p)x5 1.25% < p <= 2.25%,
// f(p) = 30+(p-2.25)x5x-1 p > 2.25%
func (r *Trace) calcLossScore(loss int) float64 {
func (t *trace) calcLossScore(loss int) float64 {
lossRate := float64(loss) / 60
if 0 < lossRate && lossRate <= 0.75 {
return 40 + (0.75-lossRate)*13
@@ -154,7 +152,7 @@ func (r *Trace) calcLossScore(loss int) float64 {
return 50
}
func (r *Trace) calcRttScore(rtt int) float64 {
func (t *trace) calcRttScore(rtt int) float64 {
avgRtt := float64(rtt) / 60
if 0 < avgRtt && avgRtt < 45.0 {
return 50
@@ -171,17 +169,18 @@ func (r *Trace) calcRttScore(rtt int) float64 {
return 0
}
// GetBestNode of all the targets of trace
func (r *Trace) GetBestNode() string {
r.targetScoreMu.Lock()
defer r.targetScoreMu.Unlock()
func (t *trace) getBestNode() (traceTarget, bool) {
t.targetScoreMu.Lock()
defer t.targetScoreMu.Unlock()
bestScore := float64(-1)
node := ""
for target, score := range r.targetScore {
if bestScore < score {
var node traceTarget
var ok bool = false
for target, score := range t.targetScore {
if bestScore*1000 < score*1000 {
bestScore = score
node = target
ok = true
}
}
return node
return node, ok
}

View File

@@ -1,4 +1,4 @@
package gtund
package main
import (
"encoding/json"
@@ -7,7 +7,11 @@ import (
"gopkg.in/yaml.v2"
)
var gConfig *Config
type Config struct {
EnableAuth bool `yaml:"enable_auth"`
Auths []AuthConfig `yaml:"auths"`
Trace string `yaml:"trace"`
ServerConfig []ServerConfig `yaml:"server"`
Log Log `yaml:"log"`
@@ -19,6 +23,12 @@ type Log struct {
Path string `yaml:"path"`
}
type AuthConfig struct {
AccessToken string `yaml:"access_token"`
ExpiredAt int64 `yaml:"expired_at"`
RateLimit int64 `yaml:"rate_limit"` // mbps
}
func ParseConfig(path string) (*Config, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
@@ -31,6 +41,10 @@ func ParseConfig(path string) (*Config, error) {
func parseConfig(content []byte) (*Config, error) {
var c Config
err := yaml.Unmarshal(content, &c)
if err != nil {
return nil, err
}
gConfig = &c
return &c, err
}
@@ -38,3 +52,7 @@ func (c *Config) String() string {
cnt, _ := json.MarshalIndent(c, "", "\t")
return string(cnt)
}
func GetConfig() *Config {
return gConfig
}

119
src/gtund/main.go Normal file
View File

@@ -0,0 +1,119 @@
package main
import (
"flag"
"fmt"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/optw/transport/transport_api"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"syscall"
"time"
)
var version = ""
var logo = `
====================================
██████ ████████ ██ ██ ███ ██
██ ██ ██ ██ ████ ██
██ ███ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██████ ██ ████
====================================
https://github.com/ICKelin/gtun
`
func init() {
go http.ListenAndServe(":6060", nil)
}
func main() {
flgVersion := flag.Bool("v", false, "print version")
flgTest := flag.Bool("t", false, "test config file")
flgConf := flag.String("c", "", "config file")
flag.Parse()
fmt.Println(logo)
if *flgVersion {
fmt.Println(version)
return
}
if *flgTest {
_, err := ParseConfig(*flgConf)
if err != nil {
fmt.Printf("FAILED: %v\n", err)
}
return
}
conf, err := ParseConfig(*flgConf)
if err != nil {
fmt.Printf("parse config file fail: %s %v\n", *flgConf, err)
return
}
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
logs.Info("%s", logo)
logs.Debug("config: %s", conf.String())
if conf.Trace != "" {
go NewTraceServer(conf.Trace).ListenAndServe()
}
for _, cfg := range conf.ServerConfig {
listener, err := transport_api.NewListen(cfg.Scheme, cfg.Listen, cfg.ListenerConfig)
if err != nil {
logs.Error("new listener fail: %v", err)
return
}
defer listener.Close()
if conf.EnableAuth {
listener.SetAuthFunc(func(token string) bool {
ok := false
auths := GetConfig().Auths
logs.Info("verify auth token %s", token)
for _, auth := range auths {
if auth.AccessToken == token {
if auth.ExpiredAt == 0 {
ok = true
} else if time.Now().Unix() < auth.ExpiredAt {
ok = true
}
break
}
}
return ok
})
}
s := NewServer(listener)
go s.Run()
}
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
for sig := range c {
switch sig {
case syscall.SIGHUP:
logs.Info("receive hup signal")
cfg, err := ParseConfig(*flgConf)
if err != nil {
logs.Warn("reload config fail: %v", err)
continue
}
// rewrite conf pointer
conf = cfg
logs.Info("reload config success")
default:
logs.Info("un handle signal %v", sig.String())
}
}
}

View File

@@ -1,17 +1,17 @@
package gtund
package main
import (
"encoding/binary"
"encoding/json"
"fmt"
"github.com/ICKelin/gtun/internal/utils"
"github.com/ICKelin/gtun/src/internal/logs"
"github.com/ICKelin/gtun/src/internal/proto"
"github.com/ICKelin/gtun/src/internal/utils"
"io"
"net"
"sync"
"time"
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/internal/proto"
"github.com/ICKelin/optw/transport"
)
@@ -66,7 +66,7 @@ func (s *Server) Run() error {
continue
}
}
return err
continue
}
logs.Info("accept connect from remote: %s", conn.RemoteAddr().String())
@@ -116,15 +116,6 @@ func (s *Server) handleStream(stream transport.Stream) {
s.tcpProxy(stream, &proxyProtocol)
case "udp":
s.udpProxy(stream, &proxyProtocol)
case "tun":
if s.devStream != nil {
s.devStream.Close()
s.devStream = stream
} else {
s.devStream = stream
go s.readDev()
}
s.tunProxy(stream, &proxyProtocol)
}
}
@@ -220,44 +211,3 @@ func (s *Server) udpProxy(stream transport.Stream, p *proto.ProxyProtocol) {
}
}
}
func (s *Server) tunProxy(stream transport.Stream, p *proto.ProxyProtocol) {
defer stream.Close()
hdr := make([]byte, 2)
for {
_, err := io.ReadFull(stream, hdr)
if err != nil {
logs.Error("read stream fail: %v", err)
break
}
nlen := binary.BigEndian.Uint16(hdr)
buf := make([]byte, nlen)
_, err = io.ReadFull(stream, buf)
if err != nil {
logs.Error("read stream body fail: %v", err)
break
}
_, err = s.dev.Write(buf)
if err != nil {
logs.Error("write to dev fail: %v", err)
return
}
}
}
func (s *Server) readDev() {
for {
buf, err := s.dev.Read()
if err != nil {
logs.Error("read interface fail: %v", err)
return
}
bytes := proto.EncodeData(buf)
_, err = s.devStream.Write(bytes)
if err != nil {
logs.Error("stream write fail: %v", err)
}
}
}

View File

@@ -1,7 +1,7 @@
package gtund
package main
import (
"github.com/ICKelin/gtun/internal/logs"
"github.com/ICKelin/gtun/src/internal/logs"
"net"
)

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
// +build windows
package logs

Some files were not shown because too many files have changed in this diff Show More