diff --git a/anylink/Dockerfile b/anylink/Dockerfile
index 34cfe13..5bbaa7d 100644
--- a/anylink/Dockerfile
+++ b/anylink/Dockerfile
@@ -1,6 +1,6 @@
# web
FROM node:16.17.1-alpine3.15 as builder_node
-ENV VERSION 0.9.1
+ENV VERSION 0.9.3
WORKDIR /web
COPY ./web /web
RUN yarn install \
@@ -8,7 +8,7 @@ RUN yarn install \
&& ls /web/ui
# server
-FROM golang:1.18-alpine as builder_golang
+FROM golang:1.19-alpine as builder_golang
#TODO 本地打包时使用镜像
ENV GOPROXY=https://goproxy.io
ENV GOOS=linux
@@ -26,13 +26,13 @@ RUN cd /anylink/server;go mod tidy;go build -o anylink -ldflags "-X main.CommitI
FROM alpine
LABEL maintainer="github.com/bjdgyc"
-ENV IPV4_CIDR="192.168.10.0/24"
+#ENV IPV4_CIDR="192.168.10.0/24"
WORKDIR /app
COPY --from=builder_golang /anylink/server/anylink /app/
COPY docker_entrypoint.sh /app/
-COPY ./server/bridge-init.sh /app/
+#COPY ./server/bridge-init.sh /app/
COPY ./server/conf /app/conf
COPY ./LICENSE /app/LICENSE
diff --git a/anylink/README.md b/anylink/README.md
index 2f00b3f..0b04917 100644
--- a/anylink/README.md
+++ b/anylink/README.md
@@ -7,17 +7,6 @@ Docker [stilleshan/anylink](https://hub.docker.com/r/stilleshan/anylink)
## 简介
基于 [bjdgyc/anylink](https://github.com/bjdgyc/anylink) 项目的 docker 镜像.
-## 更新
-- **2022-11-10** 更新`0.9.1-beta1`版 docker 镜像.
-- **2022-07-04** 更新`0.8.1`版 docker 镜像.
-- **2022-04-07** 更新`0.7.4`版 docker 镜像.
-- **2022-02-16** 更新`0.7.3`版 docker 镜像.
-- **2021-12-31** 更新`0.7.2`版 docker 镜像.
-- **2021-12-29** 更新`0.7.1`版 docker 镜像.
-- **2021-08-26** 更新`0.6.2`版 docker 镜像.
-- **2021-08-02** 更新`0.5.1`版 docker 镜像.
-- **2021-07-05** 更新`0.4.2`版 docker 镜像.
-- **2021-06-09** 更新`0.3.3`版 docker 镜像,新增同时支持 X86 和 ARM 架构.
## 部署
### docker
diff --git a/anylink/build.sh b/anylink/build.sh
index 59c1456..e8d2ce3 100644
--- a/anylink/build.sh
+++ b/anylink/build.sh
@@ -20,9 +20,10 @@ cd $cpath/web
#npm install
#npm run build
-yarn install
+yarn install --registry=https://registry.npmmirror.com
yarn run build
+
RETVAL $?
echo "编译二进制文件"
@@ -43,7 +44,7 @@ rm -rf $deploy ${deploy}.tar.gz
mkdir $deploy
cp -r server/anylink $deploy
-cp -r server/bridge-init.sh $deploy
+#cp -r server/bridge-init.sh $deploy
cp -r server/conf $deploy
cp -r systemd $deploy
diff --git a/anylink/build_docker.sh b/anylink/build_docker.sh
index fad70ff..ec94d0f 100644
--- a/anylink/build_docker.sh
+++ b/anylink/build_docker.sh
@@ -5,7 +5,8 @@ echo $ver
#docker login -u bjdgyc
-docker build -t bjdgyc/anylink .
+#docker build -t bjdgyc/anylink .
+docker build -t bjdgyc/anylink -f docker/Dockerfile .
docker tag bjdgyc/anylink:latest bjdgyc/anylink:$ver
diff --git a/anylink/doc/README.md b/anylink/doc/README.md
deleted file mode 100644
index 66c8c19..0000000
--- a/anylink/doc/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Donate
-
-> 如果您觉得 AnyLink 对你有帮助,欢迎给我们打赏,也是帮助 AnyLink 更好的发展。
-
-
使用说明:
diff --git a/anylink/server/dbdata/group.go b/anylink/server/dbdata/group.go
index ee66ac0..e664aeb 100644
--- a/anylink/server/dbdata/group.go
+++ b/anylink/server/dbdata/group.go
@@ -74,6 +74,20 @@ func GetGroupNames() []string {
return names
}
+func GetGroupNamesNormal() []string {
+ var datas []Group
+ err := FindWhere(&datas, 0, 0, "status=1")
+ if err != nil {
+ base.Error(err)
+ return nil
+ }
+ var names []string
+ for _, v := range datas {
+ names = append(names, v.Name)
+ }
+ return names
+}
+
func GetGroupNamesIds() []GroupNameId {
var datas []Group
err := Find(&datas, 0, 0)
@@ -211,6 +225,21 @@ func SetGroup(g *Group) error {
return err
}
+func GroupAuthLogin(name, pwd string, authData map[string]interface{}) error {
+ g := &Group{Auth: authData}
+ authType := g.Auth["type"].(string)
+ if _, ok := authRegistry[authType]; !ok {
+ return errors.New("未知的认证方式: " + authType)
+ }
+ auth := makeInstance(authType).(IUserAuth)
+ err := auth.checkData(g.Auth)
+ if err != nil {
+ return err
+ }
+ err = auth.checkUser(name, pwd, g)
+ return err
+}
+
func parseIpNet(s string) (string, *net.IPNet, error) {
ip, ipNet, err := net.ParseCIDR(s)
if err != nil {
diff --git a/anylink/server/dbdata/group_test.go b/anylink/server/dbdata/group_test.go
index b4b26f9..0d64c86 100644
--- a/anylink/server/dbdata/group_test.go
+++ b/anylink/server/dbdata/group_test.go
@@ -46,13 +46,14 @@ func TestGetGroupNames(t *testing.T) {
authData = map[string]interface{}{
"type": "ldap",
"ldap": map[string]interface{}{
- "addr": "192.168.8.12:389",
- "tls": true,
- "bind_name": "userfind@abc.com",
- "bind_pwd": "afdbfdsafds",
- "base_dn": "dc=abc,dc=com",
- "search_attr": "sAMAccountName",
- "member_of": "cn=vpn,cn=user,dc=abc,dc=com",
+ "addr": "192.168.8.12:389",
+ "tls": true,
+ "bind_name": "userfind@abc.com",
+ "bind_pwd": "afdbfdsafds",
+ "base_dn": "dc=abc,dc=com",
+ "object_class": "person",
+ "search_attr": "sAMAccountName",
+ "member_of": "cn=vpn,cn=user,dc=abc,dc=com",
},
}
g7 := Group{Name: "g7", ClientDns: []ValData{{Val: "114.114.114.114"}}, Auth: authData}
diff --git a/anylink/server/dbdata/ip_map.go b/anylink/server/dbdata/ip_map.go
index 98c955a..e9474dc 100644
--- a/anylink/server/dbdata/ip_map.go
+++ b/anylink/server/dbdata/ip_map.go
@@ -2,20 +2,22 @@ package dbdata
import (
"errors"
+ "net"
"time"
)
-// type IpMap struct {
-// Id int `json:"id" xorm:"pk autoincr not null"`
-// IpAddr string `json:"ip_addr" xorm:"not null unique"`
-// MacAddr string `json:"mac_addr" xorm:"not null unique"`
-// Username string `json:"username"`
-// Keep bool `json:"keep"` // 保留 ip-mac 绑定
-// KeepTime time.Time `json:"keep_time"`
-// Note string `json:"note"` // 备注
-// LastLogin time.Time `json:"last_login"`
-// UpdatedAt time.Time `json:"updated_at"`
-// }
+type IpMap struct {
+ Id int `json:"id" xorm:"pk autoincr not null"`
+ IpAddr string `json:"ip_addr" xorm:"varchar(32) not null unique"`
+ MacAddr string `json:"mac_addr" xorm:"varchar(32) not null unique"`
+ UniqueMac bool `json:"unique_mac" xorm:"Bool index"`
+ Username string `json:"username" xorm:"varchar(60)"`
+ Keep bool `json:"keep" xorm:"Bool"` // 保留 ip-mac 绑定
+ KeepTime time.Time `json:"keep_time" xorm:"DateTime"`
+ Note string `json:"note" xorm:"varchar(255)"` // 备注
+ LastLogin time.Time `json:"last_login" xorm:"DateTime"`
+ UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
+}
func SetIpMap(v *IpMap) error {
var err error
@@ -24,6 +26,13 @@ func SetIpMap(v *IpMap) error {
return errors.New("IP或MAC错误")
}
+ macHw, err := net.ParseMAC(v.MacAddr)
+ if err != nil {
+ return errors.New("MAC错误")
+ }
+ // 统一macAddr的格式
+ v.MacAddr = macHw.String()
+
v.UpdatedAt = time.Now()
if v.Id > 0 {
err = Set(v)
diff --git a/anylink/server/dbdata/setting.go b/anylink/server/dbdata/setting.go
index 964cb72..c7e0a36 100644
--- a/anylink/server/dbdata/setting.go
+++ b/anylink/server/dbdata/setting.go
@@ -21,8 +21,9 @@ type SettingSmtp struct {
}
type SettingAuditLog struct {
- LifeDay int `json:"life_day"`
- ClearTime string `json:"clear_time"`
+ AuditInterval int `json:"audit_interval"`
+ LifeDay int `json:"life_day"`
+ ClearTime string `json:"clear_time"`
}
type SettingOther struct {
@@ -48,7 +49,6 @@ func SettingSessAdd(sess *xorm.Session, data interface{}) error {
v, _ := json.Marshal(data)
s := &Setting{Name: name, Data: v}
_, err := sess.InsertOne(s)
-
return err
}
diff --git a/anylink/server/dbdata/statsinfo.go b/anylink/server/dbdata/statsinfo.go
index cef60be..0ec53e4 100644
--- a/anylink/server/dbdata/statsinfo.go
+++ b/anylink/server/dbdata/statsinfo.go
@@ -199,12 +199,6 @@ func (s *StatsInfo) getScopeDetail(scope string) (sd *ScopeDetail) {
}
sd.fsTime = sd.sTime.Format(LayoutTimeFormat)
sd.feTime = sd.eTime.Format(LayoutTimeFormat)
- // UTC
- switch base.Cfg.DbType {
- case "sqlite3", "postgres":
- sd.fsTime = sd.sTime.UTC().Format(LayoutTimeFormat)
- sd.feTime = sd.eTime.UTC().Format(LayoutTimeFormat)
- }
return
}
diff --git a/anylink/server/dbdata/tables.go b/anylink/server/dbdata/tables.go
index e0da7e0..b70e45a 100644
--- a/anylink/server/dbdata/tables.go
+++ b/anylink/server/dbdata/tables.go
@@ -29,26 +29,31 @@ type User struct {
Nickname string `json:"nickname" xorm:"varchar(255)"`
Email string `json:"email" xorm:"varchar(255)"`
// Password string `json:"password"`
- PinCode string `json:"pin_code" xorm:"varchar(32)"`
- OtpSecret string `json:"otp_secret" xorm:"varchar(255)"`
- DisableOtp bool `json:"disable_otp" xorm:"Bool"` // 禁用otp
- Groups []string `json:"groups" xorm:"Text"`
- Status int8 `json:"status" xorm:"Int"` // 1正常
- SendEmail bool `json:"send_email" xorm:"Bool"`
- CreatedAt time.Time `json:"created_at" xorm:"DateTime created"`
- UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
+ PinCode string `json:"pin_code" xorm:"varchar(32)"`
+ LimitTime *time.Time `json:"limittime,omitempty" xorm:"Datetime limittime"` // 值为null时,前端不显示
+ OtpSecret string `json:"otp_secret" xorm:"varchar(255)"`
+ DisableOtp bool `json:"disable_otp" xorm:"Bool"` // 禁用otp
+ Groups []string `json:"groups" xorm:"Text"`
+ Status int8 `json:"status" xorm:"Int"` // 1正常
+ SendEmail bool `json:"send_email" xorm:"Bool"`
+ CreatedAt time.Time `json:"created_at" xorm:"DateTime created"`
+ UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
}
-type IpMap struct {
- Id int `json:"id" xorm:"pk autoincr not null"`
- IpAddr string `json:"ip_addr" xorm:"varchar(32) not null unique"`
- MacAddr string `json:"mac_addr" xorm:"varchar(32) not null unique"`
- Username string `json:"username" xorm:"varchar(60)"`
- Keep bool `json:"keep" xorm:"Bool"` // 保留 ip-mac 绑定
- KeepTime time.Time `json:"keep_time" xorm:"DateTime"`
- Note string `json:"note" xorm:"varchar(255)"` // 备注
- LastLogin time.Time `json:"last_login" xorm:"DateTime"`
- UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
+type UserActLog struct {
+ Id int `json:"id" xorm:"pk autoincr not null"`
+ Username string `json:"username" xorm:"varchar(60)"`
+ GroupName string `json:"group_name" xorm:"varchar(60)"`
+ IpAddr string `json:"ip_addr" xorm:"varchar(32)"`
+ RemoteAddr string `json:"remote_addr" xorm:"varchar(32)"`
+ Os uint8 `json:"os" xorm:"not null default 0 Int"`
+ Client uint8 `json:"client" xorm:"not null default 0 Int"`
+ Version string `json:"version" xorm:"varchar(15)"`
+ DeviceType string `json:"device_type" xorm:"varchar(128) not null default ''"`
+ PlatformVersion string `json:"platform_version" xorm:"varchar(128) not null default ''"`
+ Status uint8 `json:"status" xorm:"not null default 0 Int"`
+ Info string `json:"info" xorm:"varchar(255) not null default ''"` // 详情
+ CreatedAt time.Time `json:"created_at" xorm:"DateTime created"`
}
type Setting struct {
diff --git a/anylink/server/dbdata/user.go b/anylink/server/dbdata/user.go
index 7834013..e05ab4d 100644
--- a/anylink/server/dbdata/user.go
+++ b/anylink/server/dbdata/user.go
@@ -104,7 +104,12 @@ func checkLocalUser(name, pwd, group string) error {
v := &User{}
err := One("Username", name, v)
if err != nil || v.Status != 1 {
- return fmt.Errorf("%s %s", name, "用户名错误")
+ switch v.Status {
+ case 0:
+ return fmt.Errorf("%s %s", name, "用户不存在或用户已停用")
+ case 2:
+ return fmt.Errorf("%s %s", name, "用户已过期")
+ }
}
// 判断用户组信息
if !utils.InArrStr(v.Groups, group) {
@@ -128,6 +133,21 @@ func checkLocalUser(name, pwd, group string) error {
return nil
}
+// 用户过期时间到达后,更新用户状态,并返回一个状态为过期的用户切片
+func CheckUserlimittime() (limitUser []interface{}) {
+ if _, err := xdb.Where("limittime <= ?", time.Now()).And("status = ?", 1).Update(&User{Status: 2}); err != nil {
+ return
+ }
+ user := make(map[int64]User)
+ if err := xdb.Where("status != ?", 1).Find(user); err != nil {
+ return
+ }
+ for _, v := range user {
+ limitUser = append(limitUser, v.Username)
+ }
+ return
+}
+
var (
userOtpMux = sync.Mutex{}
userOtp = map[string]time.Time{}
diff --git a/anylink/server/dbdata/user_act_log.go b/anylink/server/dbdata/user_act_log.go
new file mode 100644
index 0000000..ac9c506
--- /dev/null
+++ b/anylink/server/dbdata/user_act_log.go
@@ -0,0 +1,210 @@
+package dbdata
+
+import (
+ "net/url"
+ "regexp"
+ "strings"
+
+ "github.com/bjdgyc/anylink/base"
+ "github.com/ivpusic/grpool"
+ "github.com/spf13/cast"
+ "xorm.io/xorm"
+)
+
+const (
+ UserAuthFail = 0 // 认证失败
+ UserAuthSuccess = 1 // 认证成功
+ UserConnected = 2 // 连线成功
+ UserLogout = 3 // 用户登出
+ UserLogoutLose = 0 // 用户掉线
+ UserLogoutBanner = 1 // 用户banner弹窗取消
+ UserLogoutClient = 2 // 用户主动登出
+ UserLogoutTimeout = 3 // 用户超时登出
+ UserLogoutAdmin = 4 // 账号被管理员踢下线
+ UserLogoutExpire = 5 // 账号过期被踢下线
+)
+
+type UserActLogProcess struct {
+ Pool *grpool.Pool
+ StatusOps []string
+ OsOps []string
+ ClientOps []string
+ InfoOps []string
+}
+
+var (
+ UserActLogIns = &UserActLogProcess{
+ Pool: grpool.NewPool(1, 100),
+ StatusOps: []string{ // 操作类型
+ UserAuthFail: "认证失败",
+ UserAuthSuccess: "认证成功",
+ UserConnected: "连接成功",
+ UserLogout: "用户登出",
+ },
+ OsOps: []string{ // 操作系统
+ 0: "Unknown",
+ 1: "Windows",
+ 2: "macOS",
+ 3: "Linux",
+ 4: "Android",
+ 5: "iOS",
+ },
+ ClientOps: []string{ // 客户端
+ 0: "Unknown",
+ 1: "AnyConnect",
+ 2: "OpenConnect",
+ 3: "AnyLink",
+ },
+ InfoOps: []string{ // 信息
+ UserLogoutLose: "用户掉线",
+ UserLogoutBanner: "用户取消弹窗/客户端发起的logout",
+ UserLogoutClient: "用户/客户端主动断开",
+ UserLogoutTimeout: "Session过期被踢下线",
+ UserLogoutAdmin: "账号被管理员踢下线",
+ UserLogoutExpire: "账号过期被踢下线",
+ },
+ }
+)
+
+// 异步写入用户操作日志
+func (ua *UserActLogProcess) Add(u UserActLog, userAgent string) {
+ // os, client, ver
+ os_idx, client_idx, ver := ua.ParseUserAgent(userAgent)
+ u.Os = os_idx
+ u.Client = client_idx
+ u.Version = ver
+ u.RemoteAddr = strings.Split(u.RemoteAddr, ":")[0]
+ // remove extra characters
+ infoSlice := strings.Split(u.Info, " ")
+ infoLen := len(infoSlice)
+ if infoLen > 1 {
+ if u.Username == infoSlice[0] {
+ u.Info = strings.Join(infoSlice[1:], " ")
+ }
+ // delete - char
+ if infoLen > 2 && infoSlice[1] == "-" {
+ u.Info = u.Info[2:]
+ }
+ }
+ // limit the max length of char
+ u.Version = substr(u.Version, 0, 15)
+ u.DeviceType = substr(u.DeviceType, 0, 128)
+ u.PlatformVersion = substr(u.PlatformVersion, 0, 128)
+ u.Info = substr(u.Info, 0, 255)
+
+ UserActLogIns.Pool.JobQueue <- func() {
+ err := Add(u)
+ if err != nil {
+ base.Error("Add UserActLog error: ", err)
+ }
+ }
+}
+
+// 转义操作类型, 方便vue显示
+func (ua *UserActLogProcess) GetStatusOpsWithTag() interface{} {
+ type StatusTag struct {
+ Key int `json:"key"`
+ Value string `json:"value"`
+ Tag string `json:"tag"`
+ }
+ var res []StatusTag
+ for k, v := range ua.StatusOps {
+ tag := "info"
+ switch k {
+ case UserAuthFail:
+ tag = "danger"
+ case UserAuthSuccess:
+ tag = "success"
+ case UserConnected:
+ tag = ""
+ }
+ res = append(res, StatusTag{k, v, tag})
+ }
+ return res
+}
+
+func (ua *UserActLogProcess) GetInfoOpsById(id uint8) string {
+ return ua.InfoOps[id]
+}
+
+// 解析user agent
+func (ua *UserActLogProcess) ParseUserAgent(userAgent string) (os_idx, client_idx uint8, ver string) {
+ // Unknown
+ if len(userAgent) == 0 {
+ return 0, 0, ""
+ }
+ // OS
+ os_idx = 0
+ if strings.Contains(userAgent, "windows") {
+ os_idx = 1
+ } else if strings.Contains(userAgent, "mac os") || strings.Contains(userAgent, "darwin_i386") {
+ os_idx = 2
+ } else if strings.Contains(userAgent, "darwin_arm") || strings.Contains(userAgent, "apple") {
+ os_idx = 5
+ } else if strings.Contains(userAgent, "android") {
+ os_idx = 4
+ } else if strings.Contains(userAgent, "linux") {
+ os_idx = 3
+ }
+ // Client
+ client_idx = 0
+ if strings.Contains(userAgent, "anyconnect") {
+ client_idx = 1
+ } else if strings.Contains(userAgent, "openconnect") {
+ client_idx = 2
+ } else if strings.Contains(userAgent, "anylink") {
+ client_idx = 3
+ }
+ // Version
+ uaSlice := strings.Split(userAgent, " ")
+ ver = uaSlice[len(uaSlice)-1]
+ if ver[0] == 'v' {
+ ver = ver[1:]
+ }
+ if !regexp.MustCompile(`^(\d+\.?)+$`).MatchString(ver) {
+ ver = ""
+ }
+ return
+}
+
+// 清除用户操作日志
+func (ua *UserActLogProcess) ClearUserActLog(ts string) (int64, error) {
+ affected, err := xdb.Where("created_at < '" + ts + "'").Delete(&UserActLog{})
+ return affected, err
+}
+
+// 后台筛选用户操作日志
+func (ua *UserActLogProcess) GetSession(values url.Values) *xorm.Session {
+ session := xdb.Where("1=1")
+ if values.Get("username") != "" {
+ session.And("username = ?", values.Get("username"))
+ }
+ if values.Get("sdate") != "" {
+ session.And("created_at >= ?", values.Get("sdate")+" 00:00:00'")
+ }
+ if values.Get("edate") != "" {
+ session.And("created_at <= ?", values.Get("edate")+" 23:59:59'")
+ }
+ if values.Get("status") != "" {
+ session.And("status = ?", cast.ToUint8(values.Get("status"))-1)
+ }
+ if values.Get("os") != "" {
+ session.And("os = ?", cast.ToUint8(values.Get("os"))-1)
+ }
+ if values.Get("sort") == "1" {
+ session.OrderBy("id desc")
+ } else {
+ session.OrderBy("id asc")
+ }
+ return session
+}
+
+// 截取字符串
+func substr(s string, pos, length int) string {
+ runes := []rune(s)
+ l := pos + length
+ if l > len(runes) {
+ l = len(runes)
+ }
+ return string(runes[pos:l])
+}
diff --git a/anylink/server/dbdata/user_act_log_test.go b/anylink/server/dbdata/user_act_log_test.go
new file mode 100644
index 0000000..1e2b25f
--- /dev/null
+++ b/anylink/server/dbdata/user_act_log_test.go
@@ -0,0 +1,82 @@
+package dbdata
+
+import "testing"
+
+func TestParseUserAgent(t *testing.T) {
+ type args struct {
+ userAgent string
+ }
+ type res struct {
+ os_idx uint8
+ client_idx uint8
+ ver string
+ }
+ tests := []struct {
+ name string
+ args args
+ want res
+ }{
+ {
+ name: "mac os 1",
+ args: args{userAgent: "cisco anyconnect vpn agent for mac os x 4.10.05085"},
+ want: res{os_idx: 2, client_idx: 1, ver: "4.10.05085"},
+ },
+ {
+ name: "mac os 2",
+ args: args{userAgent: "anyconnect darwin_i386 4.10.05085"},
+ want: res{os_idx: 2, client_idx: 1, ver: "4.10.05085"},
+ },
+ {
+ name: "windows",
+ args: args{userAgent: "cisco anyconnect vpn agent for windows 4.8.02042"},
+ want: res{os_idx: 1, client_idx: 1, ver: "4.8.02042"},
+ },
+ {
+ name: "iPad",
+ args: args{userAgent: "anyconnect applesslvpn_darwin_arm (ipad) 4.10.04060"},
+ want: res{os_idx: 5, client_idx: 1, ver: "4.10.04060"},
+ },
+ {
+ name: "iPhone",
+ args: args{userAgent: "cisco anyconnect vpn agent for apple iphone 4.10.04060"},
+ want: res{os_idx: 5, client_idx: 1, ver: "4.10.04060"},
+ },
+ {
+ name: "android",
+ args: args{userAgent: "anyconnect android 4.10.05096"},
+ want: res{os_idx: 4, client_idx: 1, ver: "4.10.05096"},
+ },
+ {
+ name: "linux",
+ args: args{userAgent: "cisco anyconnect vpn agent for linux v7.08"},
+ want: res{os_idx: 3, client_idx: 1, ver: "7.08"},
+ },
+ {
+ name: "openconnect",
+ args: args{userAgent: "openconnect-gui 1.5.3 v7.08"},
+ want: res{os_idx: 0, client_idx: 2, ver: "7.08"},
+ },
+ {
+ name: "unknown",
+ args: args{userAgent: "unknown 1.4.3 aabcd"},
+ want: res{os_idx: 0, client_idx: 0, ver: ""},
+ },
+ {
+ name: "unknown 2",
+ args: args{userAgent: ""},
+ want: res{os_idx: 0, client_idx: 0, ver: ""},
+ },
+ {
+ name: "anylink",
+ args: args{userAgent: "anylink vpn agent for linux v1.0"},
+ want: res{os_idx: 3, client_idx: 3, ver: "1.0"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if os_idx, client_idx, ver := UserActLogIns.ParseUserAgent(tt.args.userAgent); os_idx != tt.want.os_idx || client_idx != tt.want.client_idx || ver != tt.want.ver {
+ t.Errorf("ParseUserAgent() = %v, %v, %v, want %v, %v, %v", os_idx, client_idx, ver, tt.want.os_idx, tt.want.client_idx, tt.want.ver)
+ }
+ })
+ }
+}
diff --git a/anylink/server/dbdata/user_test.go b/anylink/server/dbdata/user_test.go
index d9f3a69..fea4735 100644
--- a/anylink/server/dbdata/user_test.go
+++ b/anylink/server/dbdata/user_test.go
@@ -70,13 +70,14 @@ func TestCheckUser(t *testing.T) {
authData = map[string]interface{}{
"type": "ldap",
"ldap": map[string]interface{}{
- "addr": "192.168.8.12:389",
- "tls": true,
- "bind_name": "userfind@abc.com",
- "bind_pwd": "afdbfdsafds",
- "base_dn": "dc=abc,dc=com",
- "search_attr": "sAMAccountName",
- "member_of": "cn=vpn,cn=user,dc=abc,dc=com",
+ "addr": "192.168.8.12:389",
+ "tls": true,
+ "bind_name": "userfind@abc.com",
+ "bind_pwd": "afdbfdsafds",
+ "base_dn": "dc=abc,dc=com",
+ "object_class": "person",
+ "search_attr": "sAMAccountName",
+ "member_of": "cn=vpn,cn=user,dc=abc,dc=com",
},
}
g3 := Group{Name: group3, Status: 1, ClientDns: dns, RouteInclude: route, Auth: authData}
diff --git a/anylink/server/dbdata/userauth_ldap.go b/anylink/server/dbdata/userauth_ldap.go
index 1ade05f..e7a5f1a 100644
--- a/anylink/server/dbdata/userauth_ldap.go
+++ b/anylink/server/dbdata/userauth_ldap.go
@@ -8,19 +8,21 @@ import (
"net"
"reflect"
"regexp"
+ "strconv"
"time"
"github.com/go-ldap/ldap"
)
type AuthLdap struct {
- Addr string `json:"addr"`
- Tls bool `json:"tls"`
- BindName string `json:"bind_name"`
- BindPwd string `json:"bind_pwd"`
- BaseDn string `json:"base_dn"`
- SearchAttr string `json:"search_attr"`
- MemberOf string `json:"member_of"`
+ Addr string `json:"addr"`
+ Tls bool `json:"tls"`
+ BindName string `json:"bind_name"`
+ BindPwd string `json:"bind_pwd"`
+ BaseDn string `json:"base_dn"`
+ ObjectClass string `json:"object_class"`
+ SearchAttr string `json:"search_attr"`
+ MemberOf string `json:"member_of"`
}
func init() {
@@ -39,7 +41,7 @@ func (auth AuthLdap) checkData(authData map[string]interface{}) error {
return errors.New("LDAP的服务器地址(含端口)填写有误")
}
if auth.BindName == "" {
- return errors.New("LDAP的管理员账号不能为空")
+ return errors.New("LDAP的管理员 DN不能为空")
}
if auth.BindPwd == "" {
return errors.New("LDAP的管理员密码不能为空")
@@ -47,6 +49,9 @@ func (auth AuthLdap) checkData(authData map[string]interface{}) error {
if auth.BaseDn == "" || !ValidateDN(auth.BaseDn) {
return errors.New("LDAP的Base DN填写有误")
}
+ if auth.ObjectClass == "" {
+ return errors.New("LDAP的用户对象类填写有误")
+ }
if auth.SearchAttr == "" {
return errors.New("LDAP的用户唯一ID不能为空")
}
@@ -93,9 +98,12 @@ func (auth AuthLdap) checkUser(name, pwd string, g *Group) error {
}
err = l.Bind(auth.BindName, auth.BindPwd)
if err != nil {
- return fmt.Errorf("%s LDAP 管理员账号或密码填写有误 %s", name, err.Error())
+ return fmt.Errorf("%s LDAP 管理员 DN或密码填写有误 %s", name, err.Error())
}
- filterAttr := "(objectClass=person)"
+ if auth.ObjectClass == "" {
+ auth.ObjectClass = "person"
+ }
+ filterAttr := "(objectClass=" + auth.ObjectClass + ")"
filterAttr += "(" + auth.SearchAttr + "=" + name + ")"
if auth.MemberOf != "" {
filterAttr += "(memberOf:=" + auth.MemberOf + ")"
@@ -117,6 +125,10 @@ func (auth AuthLdap) checkUser(name, pwd string, g *Group) error {
}
return fmt.Errorf("LDAP发现 %s 用户,存在多个账号", name)
}
+ err = parseEntries(sr)
+ if err != nil {
+ return fmt.Errorf("LDAP %s 用户 %s", name, err.Error())
+ }
userDN := sr.Entries[0].DN
err = l.Bind(userDN, pwd)
if err != nil {
@@ -125,6 +137,32 @@ func (auth AuthLdap) checkUser(name, pwd string, g *Group) error {
return nil
}
+func parseEntries(sr *ldap.SearchResult) error {
+ for _, attr := range sr.Entries[0].Attributes {
+ switch attr.Name {
+ case "shadowExpire":
+ // -1 启用, 1 停用, >1 从1970-01-01至到期日的天数
+ val, _ := strconv.ParseInt(attr.Values[0], 10, 64)
+ if val == -1 {
+ return nil
+ }
+ if val == 1 {
+ return fmt.Errorf("账号已停用")
+ }
+ if val > 1 {
+ expireTime := time.Unix(val*86400, 0)
+ t := time.Date(expireTime.Year(), expireTime.Month(), expireTime.Day(), 23, 59, 59, 0, time.Local)
+ if t.Before(time.Now()) {
+ return fmt.Errorf("账号已过期(过期日期: %s)", t.Format("2006-01-02"))
+ }
+ return nil
+ }
+ return fmt.Errorf("账号shadowExpire值异常: %d", val)
+ }
+ }
+ return nil
+}
+
func ValidateDomainPort(addr string) bool {
re := regexp.MustCompile(`^([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+[A-Za-z]{2,18}\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$`)
return re.MatchString(addr)
diff --git a/anylink/server/go.mod b/anylink/server/go.mod
index acc9e92..ac5cd6c 100644
--- a/anylink/server/go.mod
+++ b/anylink/server/go.mod
@@ -1,75 +1,104 @@
module github.com/bjdgyc/anylink
-go 1.18
+go 1.19
require (
github.com/arl/statsviz v0.5.1
+ github.com/deckarep/golang-set v1.8.0
+ github.com/go-acme/lego/v4 v4.10.2
github.com/go-co-op/gocron v1.17.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-sql-driver/mysql v1.6.0
github.com/gocarina/gocsv v0.0.0-20220712153207-8b2118da4570
- github.com/golang-jwt/jwt/v4 v4.0.0
+ github.com/golang-jwt/jwt/v4 v4.2.0
github.com/google/gopacket v1.1.19
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/ivpusic/grpool v1.0.0
+ github.com/lanrenwo/lzsgo v0.0.2
github.com/lib/pq v1.10.2
- github.com/mattn/go-sqlite3 v1.14.8
+ github.com/mattn/go-sqlite3 v1.14.9
github.com/orcaman/concurrent-map v1.0.0
- github.com/pion/dtls/v2 v2.1.5
+ github.com/pion/dtls/v2 v2.2.6
github.com/pion/logging v0.2.2
+ github.com/pires/go-proxyproto v0.6.2
github.com/shirou/gopsutil v3.21.7+incompatible
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
+ github.com/spf13/cast v1.3.1
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
- github.com/stretchr/testify v1.8.0
+ github.com/stretchr/testify v1.8.1
github.com/xhit/go-simple-mail/v2 v2.10.0
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
+ github.com/xuri/excelize/v2 v2.6.1
go.uber.org/atomic v1.10.0
- golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
- golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
- golang.org/x/text v0.3.7
- golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
+ golang.org/x/crypto v0.5.0
+ golang.org/x/net v0.7.0
+ golang.org/x/text v0.7.0
+ golang.org/x/time v0.3.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
- xorm.io/xorm v1.2.2
+ xorm.io/xorm v1.3.2
+)
+
+require (
+ github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
+ github.com/cenkalti/backoff/v4 v4.2.0 // indirect
+ github.com/cloudflare/cloudflare-go v0.49.0 // indirect
+ github.com/felixge/httpsnoop v1.0.1 // indirect
+ github.com/go-jose/go-jose/v3 v3.0.0 // indirect
+ github.com/google/go-querystring v1.1.0 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
+ github.com/jmespath/go-jmespath v0.4.0 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/miekg/dns v1.1.50 // indirect
+ github.com/pion/transport/v2 v2.0.2 // indirect
+ github.com/pion/udp/v2 v2.0.1 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+ golang.org/x/tools v0.1.12 // indirect
)
require (
github.com/StackExchange/wmi v1.2.1 // indirect
+ github.com/coreos/go-iptables v0.6.0
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/felixge/httpsnoop v1.0.1 // indirect
- github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
- github.com/goccy/go-json v0.7.4 // indirect
- github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
+ github.com/goccy/go-json v0.8.1 // indirect
+ github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
- github.com/json-iterator/go v1.1.11 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
github.com/magiconair/properties v1.8.5 // indirect
- github.com/mitchellh/mapstructure v1.4.1 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
- github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
- github.com/pion/transport v0.13.0 // indirect
- github.com/pion/udp v0.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/richardlehane/mscfb v1.0.4 // indirect
+ github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/spf13/afero v1.6.0 // indirect
- github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tklauser/go-sysconf v0.3.7 // indirect
github.com/tklauser/numcpus v0.2.3 // indirect
- golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
- golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
+ github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
+ github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
+ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+ golang.org/x/sys v0.5.0 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
- gopkg.in/ini.v1 v1.62.0 // indirect
+ gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- xorm.io/builder v0.3.9 // indirect
+ xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
)
diff --git a/anylink/server/go.sum b/anylink/server/go.sum
index aa2f83a..89e6dfb 100644
--- a/anylink/server/go.sum
+++ b/anylink/server/go.sum
@@ -39,6 +39,7 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
+gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -53,6 +54,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
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/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -72,6 +75,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
+github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
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=
@@ -79,12 +84,16 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/cloudflare-go v0.49.0 h1:KqJYk/YQ5ZhmyYz1oa4kGDskfF1gVuZfqesaJ/XDLto=
+github.com/cloudflare/cloudflare-go v0.49.0/go.mod h1:h0QgcIZ3qEXwFiwfBO8sQxjVdYsLX+PfD7NFEnANaKg=
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/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
+github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -95,9 +104,12 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
+github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -115,19 +127,25 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
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/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
+github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-acme/lego/v4 v4.10.2 h1:5eW3qmda5v/LP21v1Hj70edKY1jeFZQwO617tdkwp6Q=
+github.com/go-acme/lego/v4 v4.10.2/go.mod h1:EMbf0Jmqwv94nJ5WL9qWnSXIBZnvsS9gNypansHGc6U=
github.com/go-co-op/gocron v1.17.0 h1:IixLXsti+Qo0wMvmn6Kmjp2csk2ykpkcL+EmHmST18w=
github.com/go-co-op/gocron v1.17.0/go.mod h1:IpDBSaJOVfFw7hXZuTag3SCSkqazXBBUkbQ1m1aesBs=
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-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
+github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
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-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
@@ -144,8 +162,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gocarina/gocsv v0.0.0-20220712153207-8b2118da4570 h1:n4E8KiBgNvYdtjgJbAqKov2IFv7tDkULV/2Ld3wj5Hg=
github.com/gocarina/gocsv v0.0.0-20220712153207-8b2118da4570/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
-github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k=
-github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
+github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -154,8 +172,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
-github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
+github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -187,8 +206,10 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
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-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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=
@@ -202,6 +223,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
@@ -222,9 +246,10 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
@@ -246,9 +271,15 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/consul/sdk v0.3.0/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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
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-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
+github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
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=
@@ -322,15 +353,20 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@@ -342,12 +378,15 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lanrenwo/lzsgo v0.0.2 h1:FA30LAaJFYLoaM17b+H32gA+5H+abjoomNLSA9HCbrI=
+github.com/lanrenwo/lzsgo v0.0.2/go.mod h1:oxDZy2vgi6VBGIdvL80ayRMtIyXV+TbjavVuINXZY2k=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -363,20 +402,22 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
-github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
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/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
+github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
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=
@@ -384,14 +425,18 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
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 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
@@ -427,17 +472,20 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pion/dtls/v2 v2.1.5 h1:jlh2vtIyUBShchoTDqpCCqiYCyRFJ/lvf/gQ8TALs+c=
-github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY=
+github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
+github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
-github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
-github.com/pion/transport v0.13.0 h1:KWTA5ZrQogizzYwPEciGtHPLwpAjE91FgXnyu+Hv2uY=
-github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g=
-github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o=
-github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
+github.com/pion/transport/v2 v2.0.2 h1:St+8o+1PEzPT51O9bv+tH/KYYLMNR5Vwm5Z3Qkjsywg=
+github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
+github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
+github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
+github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
+github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -462,11 +510,17 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
+github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
+github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
+github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
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/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -486,9 +540,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w=
@@ -517,6 +569,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
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=
@@ -524,12 +577,17 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
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/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ=
github.com/tklauser/go-sysconf v0.3.7 h1:HT7h4+536gjqeq1ZIJPgOl1rg1XFatQGVZWp7Py53eg=
github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4=
github.com/tklauser/numcpus v0.2.3 h1:nQ0QYpiritP6ViFhrKYsiv6VVxOpum2Gks5GhnJbS/8=
@@ -542,11 +600,18 @@ github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkug
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
+github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
+github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
+github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k=
+github.com/xuri/excelize/v2 v2.6.1/go.mod h1:tL+0m6DNwSXj/sILHbQTYsLi9IF4TW59H2EF3Yrx1AU=
+github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
+github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -596,8 +661,10 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
-golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
+golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
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=
@@ -610,6 +677,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
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/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
+golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
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=
@@ -632,8 +701,9 @@ 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 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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=
@@ -671,16 +741,18 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
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-20201201195509-5d6afe98e0b7/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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
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=
@@ -703,8 +775,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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=
@@ -754,7 +827,6 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w
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-20210124154548-22da62e12c0c/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=
@@ -765,11 +837,20 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
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-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -778,14 +859,16 @@ 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -844,14 +927,15 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
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 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -967,16 +1051,18 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
-gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
+gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -990,6 +1076,7 @@ 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=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1004,37 +1091,119 @@ layeh.com/radius v0.0.0-20210819152912-ad72663a72ab h1:05KeMI4s7jEdIfHb7QCjUr5X2
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab/go.mod h1:pFWM9De99EY9TPVyHIyA56QmoRViVck/x41WFkUlc9A=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
-modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
-modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk=
+modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
+modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
+modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
+modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
+modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
+modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
+modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
+modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
+modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
+modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
+modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
+modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
+modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
+modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
+modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
+modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
+modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
+modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
+modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
+modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
+modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
+modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
+modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
+modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
+modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
+modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
+modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
+modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
+modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
+modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
+modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
+modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
+modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
+modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
+modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
+modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8=
+modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
+modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
-modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
-modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
+modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
+modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
+modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
+modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
+modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
+modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
+modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
+modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
+modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
+modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
+modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
+modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
+modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
+modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
+modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
+modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
+modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
+modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
+modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
+modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
+modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
+modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
+modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
+modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
+modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
+modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
+modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
+modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
+modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
+modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
+modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE=
+modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM=
+modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
+modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
+modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
+modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w=
-modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A=
+modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE=
+modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
-modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI=
+modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
+modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
-xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
-xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
-xorm.io/xorm v1.2.2 h1:FFBOEvJ++8fYBA9cywf2sxDVmFktl1SpJzTAG1ab06Y=
-xorm.io/xorm v1.2.2/go.mod h1:fTG8tSjk6O1BYxwuohZUK+S1glnRycsCF05L1qQyEU0=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
+xorm.io/xorm v1.3.2 h1:uTRRKF2jYzbZ5nsofXVUx6ncMaek+SHjWYtCXyZo1oM=
+xorm.io/xorm v1.3.2/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
diff --git a/anylink/server/handler/dtls.go b/anylink/server/handler/dtls.go
index 7501246..56e54a9 100644
--- a/anylink/server/handler/dtls.go
+++ b/anylink/server/handler/dtls.go
@@ -66,9 +66,13 @@ func startDtls() {
go func() {
// time.Sleep(1 * time.Second)
cc := conn.(*dtls.Conn)
- sessid := hex.EncodeToString(cc.ConnectionState().SessionID)
- sess := sessdata.Dtls2Sess(sessid)
- LinkDtls(conn, sess.CSess)
+ did := hex.EncodeToString(cc.ConnectionState().SessionID)
+ cSess := sessdata.Dtls2CSess(did)
+ if cSess == nil {
+ conn.Close()
+ return
+ }
+ LinkDtls(conn, cSess)
}()
}
}
diff --git a/anylink/server/handler/link_auth.go b/anylink/server/handler/link_auth.go
index 5a8138d..2e9bf8f 100644
--- a/anylink/server/handler/link_auth.go
+++ b/anylink/server/handler/link_auth.go
@@ -7,6 +7,7 @@ import (
"io"
"net"
"net/http"
+ "net/http/httputil"
"strings"
"text/template"
@@ -18,11 +19,16 @@ import (
var profileHash = ""
func LinkAuth(w http.ResponseWriter, r *http.Request) {
+ // TODO 调试信息输出
+ if base.GetLogLevel() == base.LogLevelTrace {
+ hd, _ := httputil.DumpRequest(r, true)
+ base.Trace("LinkAuth: ", string(hd))
+ }
// 判断anyconnect客户端
userAgent := strings.ToLower(r.UserAgent())
xAggregateAuth := r.Header.Get("X-Aggregate-Auth")
xTranscendVersion := r.Header.Get("X-Transcend-Version")
- if !((strings.Contains(userAgent, "anyconnect") || strings.Contains(userAgent, "openconnect")) &&
+ if !((strings.Contains(userAgent, "anyconnect") || strings.Contains(userAgent, "openconnect") || strings.Contains(userAgent, "anylink")) &&
xAggregateAuth == "1" && xTranscendVersion == "1") {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "error request")
@@ -43,7 +49,6 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
return
}
// fmt.Printf("%+v \n", cr)
-
setCommonHeader(w)
if cr.Type == "logout" {
// 退出删除session信息
@@ -56,7 +61,7 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
if cr.Type == "init" {
w.WriteHeader(http.StatusOK)
- data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames()}
+ data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNamesNormal()}
tplRequest(tpl_request, w, data)
return
}
@@ -66,16 +71,32 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
return
}
-
+ // 用户活动日志
+ ua := dbdata.UserActLog{
+ Username: cr.Auth.Username,
+ GroupName: cr.GroupSelect,
+ RemoteAddr: r.RemoteAddr,
+ Status: dbdata.UserAuthSuccess,
+ DeviceType: cr.DeviceId.DeviceType,
+ PlatformVersion: cr.DeviceId.PlatformVersion,
+ }
// TODO 用户密码校验
err = dbdata.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect)
if err != nil {
base.Warn(err)
+ ua.Info = err.Error()
+ ua.Status = dbdata.UserAuthFail
+ dbdata.UserActLogIns.Add(ua, userAgent)
+
w.WriteHeader(http.StatusOK)
- data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames(), Error: "用户名或密码错误"}
+ data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNamesNormal(), Error: "用户名或密码错误"}
+ if base.Cfg.DisplayError {
+ data.Error = err.Error()
+ }
tplRequest(tpl_request, w, data)
return
}
+ dbdata.UserActLogIns.Add(ua, userAgent)
// if !ok {
// w.WriteHeader(http.StatusOK)
// data := RequestData{Group: cr.GroupSelect, Groups: base.Cfg.UserGroups, Error: "请先激活用户"}
@@ -87,29 +108,38 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
sess := sessdata.NewSession("")
sess.Username = cr.Auth.Username
sess.Group = cr.GroupSelect
- sess.MacAddr = strings.ToLower(cr.MacAddressList.MacAddress)
+ oriMac := cr.MacAddressList.MacAddress
sess.UniqueIdGlobal = cr.DeviceId.UniqueIdGlobal
+ sess.UserAgent = userAgent
+ sess.DeviceType = ua.DeviceType
+ sess.PlatformVersion = ua.PlatformVersion
+ sess.RemoteAddr = r.RemoteAddr
// 获取客户端mac地址
- macHw, err := net.ParseMAC(sess.MacAddr)
+ sess.UniqueMac = true
+ macHw, err := net.ParseMAC(oriMac)
if err != nil {
var sum [16]byte
if sess.UniqueIdGlobal != "" {
sum = md5.Sum([]byte(sess.UniqueIdGlobal))
} else {
sum = md5.Sum([]byte(sess.Token))
+ sess.UniqueMac = false
}
macHw = sum[0:5] // 5个byte
macHw = append([]byte{0x02}, macHw...)
sess.MacAddr = macHw.String()
}
sess.MacHw = macHw
+ // 统一macAddr的格式
+ sess.MacAddr = macHw.String()
+
other := &dbdata.SettingOther{}
_ = dbdata.SettingGet(other)
rd := RequestData{SessionId: sess.Sid, SessionToken: sess.Sid + "@" + sess.Token,
Banner: other.Banner, ProfileHash: profileHash}
w.WriteHeader(http.StatusOK)
tplRequest(tpl_complete, w, rd)
- base.Debug("login", cr.Auth.Username)
+ base.Debug("login", cr.Auth.Username, userAgent)
}
const (
diff --git a/anylink/server/handler/link_base.go b/anylink/server/handler/link_base.go
index 17efbb3..1afcc92 100644
--- a/anylink/server/handler/link_base.go
+++ b/anylink/server/handler/link_base.go
@@ -44,6 +44,7 @@ type macAddressList struct {
func setCommonHeader(w http.ResponseWriter) {
// Content-Length Date 默认已经存在
+ w.Header().Set("Server", "AnyLink")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store,no-cache")
w.Header().Set("Pragma", "no-cache")
diff --git a/anylink/server/handler/link_cstp.go b/anylink/server/handler/link_cstp.go
index 97a9fb3..ba947e7 100644
--- a/anylink/server/handler/link_cstp.go
+++ b/anylink/server/handler/link_cstp.go
@@ -7,6 +7,7 @@ import (
"time"
"github.com/bjdgyc/anylink/base"
+ "github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
)
@@ -14,7 +15,7 @@ import (
func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSession) {
base.Debug("LinkCstp connect ip:", cSess.IpAddr, "user:", cSess.Username, "rip:", conn.RemoteAddr())
defer func() {
- base.Debug("LinkCstp return", cSess.IpAddr)
+ base.Debug("LinkCstp return", cSess.Username, cSess.IpAddr)
_ = conn.Close()
cSess.Close()
}()
@@ -33,14 +34,14 @@ func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessio
// 设置超时限制
err = conn.SetReadDeadline(utils.NowSec().Add(dead))
if err != nil {
- base.Error("SetDeadline: ", err)
+ base.Error("SetDeadline: ", cSess.Username, err)
return
}
// hdata := make([]byte, BufferSize)
pl := getPayload()
n, err = bufRW.Read(pl.Data)
if err != nil {
- base.Error("read hdata: ", err)
+ base.Error("read hdata: ", cSess.Username, err)
return
}
@@ -55,7 +56,8 @@ func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessio
// do nothing
// base.Debug("recv keepalive", cSess.IpAddr)
case 0x05: // DISCONNECT
- base.Debug("DISCONNECT", cSess.IpAddr)
+ cSess.UserLogoutCode = dbdata.UserLogoutClient
+ base.Debug("DISCONNECT", cSess.Username, cSess.IpAddr)
return
case 0x03: // DPD-REQ
// base.Debug("recv DPD-REQ", cSess.IpAddr)
@@ -64,13 +66,28 @@ func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessio
return
}
case 0x04:
- // log.Println("recv DPD-RESP")
+ // log.Println("recv DPD-RESP")
+ case 0x08: // decompress
+ if cSess.CstpPickCmp == nil {
+ continue
+ }
+ dst := getByteFull()
+ nn, err := cSess.CstpPickCmp.Uncompress(pl.Data[8:], *dst)
+ if err != nil {
+ putByte(dst)
+ base.Error("cstp decompress error", err, nn)
+ continue
+ }
+ binary.BigEndian.PutUint16(pl.Data[4:6], uint16(nn))
+ pl.Data = append(pl.Data[:8], (*dst)[:nn]...)
+ putByte(dst)
+ fallthrough
case 0x00: // DATA
// 获取数据长度
dataLen = binary.BigEndian.Uint16(pl.Data[4:6]) // 4,5
// 修复 cstp 数据长度溢出报错
if 8+dataLen > BufferSize {
- base.Error("recv error dataLen", dataLen)
+ base.Error("recv error dataLen", cSess.Username, dataLen)
continue
}
// 去除数据头
@@ -87,7 +104,7 @@ func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessio
func cstpWrite(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSession) {
defer func() {
- base.Debug("cstpWrite return", cSess.IpAddr)
+ base.Debug("cstpWrite return", cSess.Username, cSess.IpAddr)
_ = conn.Close()
cSess.Close()
}()
@@ -110,16 +127,31 @@ func cstpWrite(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessi
}
if pl.PType == 0x00 {
- // 获取数据长度
- l := len(pl.Data)
- // 先扩容 +8
- pl.Data = pl.Data[:l+8]
- // 数据后移
- copy(pl.Data[8:], pl.Data)
- // 添加头信息
- copy(pl.Data[:8], plHeader)
- // 更新头长度
- binary.BigEndian.PutUint16(pl.Data[4:6], uint16(l))
+ isCompress := false
+ if cSess.CstpPickCmp != nil && len(pl.Data) > base.Cfg.NoCompressLimit {
+ dst := getByteFull()
+ size, err := cSess.CstpPickCmp.Compress(pl.Data, (*dst)[8:])
+ if err == nil && size < len(pl.Data) {
+ copy((*dst)[:8], plHeader)
+ binary.BigEndian.PutUint16((*dst)[4:6], uint16(size))
+ (*dst)[6] = 0x08
+ pl.Data = append(pl.Data[:0], (*dst)[:size+8]...)
+ isCompress = true
+ }
+ putByte(dst)
+ }
+ if !isCompress {
+ // 获取数据长度
+ l := len(pl.Data)
+ // 先扩容 +8
+ pl.Data = pl.Data[:l+8]
+ // 数据后移
+ copy(pl.Data[8:], pl.Data)
+ // 添加头信息
+ copy(pl.Data[:8], plHeader)
+ // 更新头长度
+ binary.BigEndian.PutUint16(pl.Data[4:6], uint16(l))
+ }
} else {
pl.Data = append(pl.Data[:0], plHeader...)
// 设置头类型
@@ -128,7 +160,7 @@ func cstpWrite(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSessi
n, err = conn.Write(pl.Data)
if err != nil {
- base.Error("write err", err)
+ base.Error("write err", cSess.Username, err)
return
}
diff --git a/anylink/server/handler/link_dtls.go b/anylink/server/handler/link_dtls.go
index 23c6e86..34fe578 100644
--- a/anylink/server/handler/link_dtls.go
+++ b/anylink/server/handler/link_dtls.go
@@ -5,6 +5,7 @@ import (
"time"
"github.com/bjdgyc/anylink/base"
+ "github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
)
@@ -19,7 +20,7 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
}
defer func() {
- base.Debug("LinkDtls return", cSess.IpAddr)
+ base.Debug("LinkDtls return", cSess.Username, cSess.IpAddr)
_ = conn.Close()
dSess.Close()
}()
@@ -35,14 +36,14 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
for {
err = conn.SetReadDeadline(utils.NowSec().Add(dead))
if err != nil {
- base.Error("SetDeadline: ", err)
+ base.Error("SetDeadline: ", cSess.Username, err)
return
}
pl := getPayload()
n, err = conn.Read(pl.Data)
if err != nil {
- base.Error("read hdata: ", err)
+ base.Error("read hdata: ", cSess.Username, err)
return
}
@@ -57,7 +58,8 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
// do nothing
// base.Debug("recv keepalive", cSess.IpAddr)
case 0x05: // DISCONNECT
- base.Debug("DISCONNECT DTLS", cSess.IpAddr)
+ cSess.UserLogoutCode = dbdata.UserLogoutClient
+ base.Debug("DISCONNECT DTLS", cSess.Username, cSess.IpAddr)
return
case 0x03: // DPD-REQ
// base.Debug("recv DPD-REQ", cSess.IpAddr)
@@ -66,7 +68,22 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
return
}
case 0x04:
- // base.Debug("recv DPD-RESP", cSess.IpAddr)
+ // base.Debug("recv DPD-RESP", cSess.IpAddr)
+ case 0x08: // decompress
+ if cSess.DtlsPickCmp == nil {
+ continue
+ }
+ dst := getByteFull()
+ nn, err := cSess.DtlsPickCmp.Uncompress(pl.Data[1:], *dst)
+ if err != nil {
+ putByte(dst)
+ base.Error("dtls decompress error", err, n)
+ continue
+ }
+ pl.Data = append(pl.Data[:1], (*dst)[:nn]...)
+ putByte(dst)
+ n = nn + 1
+ fallthrough
case 0x00: // DATA
// 去除数据头
// copy(pl.Data, pl.Data[1:n])
@@ -83,7 +100,7 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
func dtlsWrite(conn net.Conn, dSess *sessdata.DtlsSession, cSess *sessdata.ConnSession) {
defer func() {
- base.Debug("dtlsWrite return", cSess.IpAddr)
+ base.Debug("dtlsWrite return", cSess.Username, cSess.IpAddr)
_ = conn.Close()
dSess.Close()
}()
@@ -106,21 +123,35 @@ func dtlsWrite(conn net.Conn, dSess *sessdata.DtlsSession, cSess *sessdata.ConnS
// header = []byte{payload.PType}
if pl.PType == 0x00 { // data
- // 获取数据长度
- l := len(pl.Data)
- // 先扩容 +1
- pl.Data = pl.Data[:l+1]
- // 数据后移
- copy(pl.Data[1:], pl.Data)
- // 添加头信息
- pl.Data[0] = pl.PType
+ isCompress := false
+ if cSess.DtlsPickCmp != nil && len(pl.Data) > base.Cfg.NoCompressLimit {
+ dst := getByteFull()
+ size, err := cSess.DtlsPickCmp.Compress(pl.Data, (*dst)[1:])
+ if err == nil && size < len(pl.Data) {
+ (*dst)[0] = 0x08
+ pl.Data = append(pl.Data[:0], (*dst)[:size+1]...)
+ isCompress = true
+ }
+ putByte(dst)
+ }
+ // 未压缩
+ if !isCompress {
+ // 获取数据长度
+ l := len(pl.Data)
+ // 先扩容 +1
+ pl.Data = pl.Data[:l+1]
+ // 数据后移
+ copy(pl.Data[1:], pl.Data)
+ // 添加头信息
+ pl.Data[0] = pl.PType
+ }
} else {
// 设置头类型
pl.Data = append(pl.Data[:0], pl.PType)
}
n, err := conn.Write(pl.Data)
if err != nil {
- base.Error("write err", err)
+ base.Error("write err", cSess.Username, err)
return
}
diff --git a/anylink/server/handler/link_home.go b/anylink/server/handler/link_home.go
index 9669d26..a2dc30b 100644
--- a/anylink/server/handler/link_home.go
+++ b/anylink/server/handler/link_home.go
@@ -13,6 +13,7 @@ func LinkHome(w http.ResponseWriter, r *http.Request) {
// fmt.Println(r.RemoteAddr)
// hu, _ := httputil.DumpRequest(r, true)
// fmt.Println("DumpHome: ", string(hu))
+ w.Header().Set("Server", "AnyLinkOpenSource")
connection := strings.ToLower(r.Header.Get("Connection"))
userAgent := strings.ToLower(r.UserAgent())
if connection == "close" && (strings.Contains(userAgent, "anyconnect") || strings.Contains(userAgent, "openconnect")) {
@@ -21,11 +22,13 @@ func LinkHome(w http.ResponseWriter, r *http.Request) {
return
}
index := &dbdata.SettingOther{}
- dbdata.SettingGet(index)
+ if err := dbdata.SettingGet(index); err != nil {
+ return
+ }
w.WriteHeader(http.StatusOK)
if index.Homeindex == "" {
index.Homeindex = "AnyLink 是一个企业级远程办公 SSL VPN 软件,可以支持多人同时在线使用。"
- }
+ }
fmt.Fprintln(w, index.Homeindex)
}
diff --git a/anylink/server/handler/link_tap.go b/anylink/server/handler/link_tap.go
index 2e3062b..67972ff 100644
--- a/anylink/server/handler/link_tap.go
+++ b/anylink/server/handler/link_tap.go
@@ -175,7 +175,7 @@ func allTapWrite(ifce LinkDriver, cSess *sessdata.ConnSession) {
return
}
- putPayload(pl)
+ putPayloadInBefore(cSess, pl)
}
}
diff --git a/anylink/server/handler/link_tun.go b/anylink/server/handler/link_tun.go
index 2f0045c..0ba7c76 100644
--- a/anylink/server/handler/link_tun.go
+++ b/anylink/server/handler/link_tun.go
@@ -5,6 +5,7 @@ import (
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/sessdata"
+ "github.com/coreos/go-iptables/iptables"
"github.com/songgao/water"
)
@@ -26,6 +27,28 @@ func checkTun() {
if err != nil {
base.Fatal("testTun err: ", err)
}
+ //开启服务器转发
+ if err := execCmd([]string{"sysctl -w net.ipv4.ip_forward=1"}); err != nil {
+ base.Error(err)
+ }
+ if base.Cfg.IptablesNat {
+ //添加NAT转发规则
+ ipt, err := iptables.New()
+ if err != nil {
+ base.Error(err)
+ return
+ }
+ natRule := []string{"-s", base.Cfg.Ipv4CIDR, "-o", base.Cfg.Ipv4Master, "-j", "MASQUERADE"}
+ forwardRule := []string{"-j", "ACCEPT"}
+ if natExists, _ := ipt.Exists("nat", "POSTROUTING", natRule...); !natExists {
+ ipt.Insert("nat", "POSTROUTING", 1, natRule...)
+ }
+ if forwardExists, _ := ipt.Exists("filter", "FORWARD", forwardRule...); !forwardExists {
+ ipt.Insert("filter", "FORWARD", 1, forwardRule...)
+ }
+ base.Info(ipt.List("nat", "POSTROUTING"))
+ base.Info(ipt.List("filter", "FORWARD"))
+ }
}
// 创建tun网卡
@@ -85,7 +108,7 @@ func tunWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
return
}
- putPayload(pl)
+ putPayloadInBefore(cSess, pl)
}
}
diff --git a/anylink/server/handler/link_tunnel.go b/anylink/server/handler/link_tunnel.go
index 16373b2..4550854 100644
--- a/anylink/server/handler/link_tunnel.go
+++ b/anylink/server/handler/link_tunnel.go
@@ -6,6 +6,7 @@ import (
"log"
"net"
"net/http"
+ "net/http/httputil"
"os"
"strings"
"text/template"
@@ -34,9 +35,10 @@ func HttpAddHeader(w http.ResponseWriter, key string, value string) {
func LinkTunnel(w http.ResponseWriter, r *http.Request) {
// TODO 调试信息输出
- // hd, _ := httputil.DumpRequest(r, true)
- // fmt.Println("DumpRequest: ", string(hd))
- // fmt.Println("LinkTunnel", r.RemoteAddr)
+ if base.GetLogLevel() == base.LogLevelTrace {
+ hd, _ := httputil.DumpRequest(r, true)
+ base.Trace("LinkTunnel: ", string(hd))
+ }
// 判断session-token的值
cookie, err := r.Cookie("webvpn")
@@ -69,6 +71,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
cSess.SetMtu(cstpMtu)
cSess.MasterSecret = masterSecret
cSess.RemoteAddr = r.RemoteAddr
+ cSess.UserAgent = strings.ToLower(r.UserAgent())
cSess.LocalIp = net.ParseIP(localIp)
cstpKeepalive := base.Cfg.CstpKeepalive
cstpDpd := base.Cfg.CstpDpd
@@ -89,6 +92,14 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile)
+ // 压缩
+ if cmpName, ok := cSess.SetPickCmp("cstp", r.Header.Get("X-Cstp-Accept-Encoding")); ok {
+ HttpSetHeader(w, "X-CSTP-Content-Encoding", cmpName)
+ }
+ if cmpName, ok := cSess.SetPickCmp("dtls", r.Header.Get("X-Dtls-Accept-Encoding")); ok {
+ HttpSetHeader(w, "X-DTLS-Content-Encoding", cmpName)
+ }
+
// 返回客户端数据
HttpSetHeader(w, "Server", fmt.Sprintf("%s %s", base.APP_NAME, base.APP_VER))
HttpSetHeader(w, "X-CSTP-Version", "1")
@@ -125,7 +136,8 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
for _, v := range cSess.Group.RouteExclude {
HttpAddHeader(w, "X-CSTP-Split-Exclude", v.IpMask)
}
- HttpSetHeader(w, "X-CSTP-Lease-Duration", fmt.Sprintf("%d", base.Cfg.IpLease)) // ip地址租期
+
+ HttpSetHeader(w, "X-CSTP-Lease-Duration", "1209600") // ip地址租期
HttpSetHeader(w, "X-CSTP-Session-Timeout", "none")
HttpSetHeader(w, "X-CSTP-Session-Timeout-Alert-Interval", "60")
HttpSetHeader(w, "X-CSTP-Session-Timeout-Remaining", "none")
@@ -134,8 +146,10 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
HttpSetHeader(w, "X-CSTP-Keep", "true")
HttpSetHeader(w, "X-CSTP-Tunnel-All-DNS", "false")
- HttpSetHeader(w, "X-CSTP-Rekey-Time", "172800")
+ HttpSetHeader(w, "X-CSTP-Rekey-Time", "43200") // 172800
HttpSetHeader(w, "X-CSTP-Rekey-Method", "new-tunnel")
+ HttpSetHeader(w, "X-DTLS-Rekey-Time", "43200")
+ HttpSetHeader(w, "X-DTLS-Rekey-Method", "new-tunnel")
HttpSetHeader(w, "X-CSTP-DPD", fmt.Sprintf("%d", cstpDpd))
HttpSetHeader(w, "X-CSTP-Keepalive", fmt.Sprintf("%d", cstpKeepalive))
@@ -150,7 +164,6 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
HttpSetHeader(w, "X-DTLS-Port", dtlsPort)
HttpSetHeader(w, "X-DTLS-DPD", fmt.Sprintf("%d", cstpDpd))
HttpSetHeader(w, "X-DTLS-Keepalive", fmt.Sprintf("%d", cstpKeepalive))
- HttpSetHeader(w, "X-DTLS-Rekey-Time", "5400")
HttpSetHeader(w, "X-DTLS12-CipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256")
HttpSetHeader(w, "X-CSTP-License", "accept")
@@ -194,6 +207,15 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
base.Error(err)
return
}
+ dbdata.UserActLogIns.Add(dbdata.UserActLog{
+ Username: sess.Username,
+ GroupName: sess.Group,
+ IpAddr: cSess.IpAddr.String(),
+ RemoteAddr: cSess.RemoteAddr,
+ DeviceType: sess.DeviceType,
+ PlatformVersion: sess.PlatformVersion,
+ Status: dbdata.UserConnected,
+ }, cSess.UserAgent)
go LinkCstp(conn, bufRW, cSess)
}
diff --git a/anylink/server/handler/payload.go b/anylink/server/handler/payload.go
index 626ae6e..e5ed545 100644
--- a/anylink/server/handler/payload.go
+++ b/anylink/server/handler/payload.go
@@ -15,11 +15,6 @@ func payloadIn(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
// 校验不通过直接丢弃
return false
}
- if base.Cfg.AuditInterval >= 0 {
- cSess.IpAuditPool.JobQueue <- func() {
- logAudit(cSess, pl)
- }
- }
}
closed := false
@@ -32,6 +27,15 @@ func payloadIn(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
return closed
}
+func putPayloadInBefore(cSess *sessdata.ConnSession, pl *sessdata.Payload) {
+ // 异步审计日志
+ if base.Cfg.AuditInterval >= 0 {
+ auditPayload.Add(cSess.Username, pl)
+ return
+ }
+ putPayload(pl)
+}
+
func payloadOut(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
dSess := cSess.GetDtlsSession()
if dSess == nil {
diff --git a/anylink/server/handler/payload_access_audit.go b/anylink/server/handler/payload_access_audit.go
index 0ad9ea4..4384352 100644
--- a/anylink/server/handler/payload_access_audit.go
+++ b/anylink/server/handler/payload_access_audit.go
@@ -3,13 +3,13 @@ package handler
import (
"crypto/md5"
"encoding/binary"
- "encoding/hex"
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
+ "github.com/ivpusic/grpool"
"github.com/songgao/water/waterutil"
)
@@ -20,73 +20,92 @@ const (
acc_proto_http
)
-// 保存批量的审计日志
+var (
+ auditPayload *AuditPayload
+ logBatch *LogBatch
+)
+
+// 分析审计日志
+type AuditPayload struct {
+ Pool *grpool.Pool
+ IpAuditMap utils.IMaps
+}
+
+// 保存审计日志
type LogBatch struct {
- Logs []dbdata.AccessAudit
+ Logs []dbdata.AccessAudit
+ LogChan chan dbdata.AccessAudit
}
-// 日志池
-type LogSink struct {
- logChan chan dbdata.AccessAudit
- autoCommitChan chan *LogBatch // 超时通知
+// 异步写入pool
+func (p *AuditPayload) Add(userName string, pl *sessdata.Payload) {
+ select {
+ case p.Pool.JobQueue <- func() {
+ logAudit(userName, pl)
+ }:
+ default:
+ putPayload(pl)
+ base.Error("AccessAudit: AuditPayload channel is full")
+ }
}
-var logAuditSink *LogSink
-
-// 写入日志通道
-func logAuditWrite(aa dbdata.AccessAudit) {
- logAuditSink.logChan <- aa
+// 数据落盘
+func (l *LogBatch) Write() {
+ if len(l.Logs) == 0 {
+ return
+ }
+ _ = dbdata.AddBatch(l.Logs)
+ l.Reset()
}
-// 批量写入数据表
+// 清空数据
+func (l *LogBatch) Reset() {
+ l.Logs = []dbdata.AccessAudit{}
+}
+
+// 开启批量写入数据功能
func logAuditBatch() {
if base.Cfg.AuditInterval < 0 {
return
}
- logAuditSink = &LogSink{
- logChan: make(chan dbdata.AccessAudit, 1000),
- autoCommitChan: make(chan *LogBatch, 10),
+ auditPayload = &AuditPayload{
+ Pool: grpool.NewPool(10, 10240),
+ IpAuditMap: utils.NewMap("cmap", 0),
+ }
+ logBatch = &LogBatch{
+ LogChan: make(chan dbdata.AccessAudit, 10240),
}
var (
- limit = 100 // 超过上限批量写入数据表
- logAudit dbdata.AccessAudit
- logBatch *LogBatch
- commitTimer *time.Timer // 超时自动提交
- timeOutBatch *LogBatch
+ limit = 100 // 超过上限批量写入数据表
+ outTime = time.NewTimer(time.Second)
+ accessAudit = dbdata.AccessAudit{}
)
+
for {
+ // 重置超时 时间
+ outTime.Reset(time.Second * 1)
select {
- case logAudit = <-logAuditSink.logChan:
- if logBatch == nil {
- logBatch = &LogBatch{}
- commitTimer = time.AfterFunc(
- 1*time.Second, func(logBatch *LogBatch) func() {
- return func() {
- logAuditSink.autoCommitChan <- logBatch
- }
- }(logBatch),
- )
- }
- logBatch.Logs = append(logBatch.Logs, logAudit)
+ case accessAudit = <-logBatch.LogChan:
+ logBatch.Logs = append(logBatch.Logs, accessAudit)
if len(logBatch.Logs) >= limit {
- commitTimer.Stop()
- _ = dbdata.AddBatch(logBatch.Logs)
- logBatch = nil
+ if !outTime.Stop() {
+ <-outTime.C
+ }
+ logBatch.Write()
}
- case timeOutBatch = <-logAuditSink.autoCommitChan:
- if timeOutBatch != logBatch {
- continue
- }
- if logBatch != nil {
- _ = dbdata.AddBatch(logBatch.Logs)
- }
- logBatch = nil
+ case <-outTime.C:
+ logBatch.Write()
}
}
}
// 解析IP包的数据
-func logAudit(cSess *sessdata.ConnSession, pl *sessdata.Payload) {
+func logAudit(userName string, pl *sessdata.Payload) {
+ defer putPayload(pl)
+
+ if !(pl.LType == sessdata.LTypeIPData && pl.PType == 0x00) {
+ return
+ }
ipProto := waterutil.IPv4Protocol(pl.Data)
// 访问协议
var accessProto uint8
@@ -109,79 +128,48 @@ func logAudit(cSess *sessdata.ConnSession, pl *sessdata.Payload) {
copy(key[:16], ipSrc)
copy(key[16:32], ipDst)
binary.BigEndian.PutUint16(key[32:34], ipPort)
+ key[34] = byte(accessProto)
+ copy(key[35:51], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
info := ""
nu := utils.NowSec().Unix()
if ipProto == waterutil.TCP {
- plData := waterutil.IPv4Payload(pl.Data)
- if len(plData) < 14 {
+ tcpPlData := waterutil.IPv4Payload(pl.Data)
+ // 24 (ACK PSH)
+ if len(tcpPlData) < 14 || tcpPlData[13] != 24 {
return
}
- flags := plData[13]
- switch flags {
- case flags & 0x20:
- // URG
- return
- case flags & 0x14:
- // RST ACK
- return
- case flags & 0x12:
- // SYN ACK
- return
- case flags & 0x11:
- // Client FIN
- return
- case flags & 0x10:
- // ACK
- return
- case flags & 0x08:
- // PSH
- return
- case flags & 0x04:
- // RST
- return
- case flags & 0x02:
- // SYN
- return
- case flags & 0x01:
- // FIN
- return
- case flags & 0x18:
- // PSH ACK
- accessProto, info = onTCP(plData)
+ accessProto, info = onTCP(tcpPlData)
+ // HTTPS or HTTP
+ if accessProto != acc_proto_tcp {
+ // 提前存储只含ip数据的key, 避免即记录域名又记录一笔IP数据的记录
+ ipKey := make([]byte, 51)
+ copy(ipKey, key)
+ ipS := utils.BytesToString(ipKey)
+ auditPayload.IpAuditMap.Set(ipS, nu)
+
+ key[34] = byte(accessProto)
+ // 存储含域名的key
if info != "" {
- // 提前存储只含ip数据的key, 避免即记录域名又记录一笔IP数据的记录
- ipKey := make([]byte, 51)
- copy(ipKey, key)
- ipS := utils.BytesToString(ipKey)
- cSess.IpAuditMap.Set(ipS, nu)
- // 存储含域名的key
- key[34] = byte(accessProto)
md5Sum := md5.Sum([]byte(info))
- copy(key[35:51], hex.EncodeToString(md5Sum[:]))
+ copy(key[35:51], md5Sum[:])
}
- case flags & 0x19:
- // URG
- return
- case flags & 0xC2:
- // SYN-ECE-CWR
- return
}
}
s := utils.BytesToString(key)
// 判断已经存在,并且没有过期
- v, ok := cSess.IpAuditMap.Get(s)
+ v, ok := auditPayload.IpAuditMap.Get(s)
if ok && nu-v.(int64) < int64(base.Cfg.AuditInterval) {
// 回收byte对象
putByte51(b)
return
}
- cSess.IpAuditMap.Set(s, nu)
+ auditPayload.IpAuditMap.Set(s, nu)
audit := dbdata.AccessAudit{
- Username: cSess.Username,
+ Username: userName,
Protocol: uint8(ipProto),
Src: ipSrc.String(),
Dst: ipDst.String(),
@@ -190,5 +178,11 @@ func logAudit(cSess *sessdata.ConnSession, pl *sessdata.Payload) {
AccessProto: accessProto,
Info: info,
}
- logAuditWrite(audit)
+
+ select {
+ case logBatch.LogChan <- audit:
+ default:
+ base.Error("AccessAudit: LogChan channel is full")
+ return
+ }
}
diff --git a/anylink/server/handler/payload_tcp_parser.go b/anylink/server/handler/payload_tcp_parser.go
index bece94d..6d3c4cc 100644
--- a/anylink/server/handler/payload_tcp_parser.go
+++ b/anylink/server/handler/payload_tcp_parser.go
@@ -21,7 +21,7 @@ func onTCP(payload []byte) (uint8, string) {
}
data := payload[ihl:]
for _, parser := range tcpParsers {
- if proto, info := parser(data); info != "" {
+ if proto, info := parser(data); proto != acc_proto_tcp {
return proto, info
}
}
@@ -29,8 +29,7 @@ func onTCP(payload []byte) (uint8, string) {
}
func sniNewParser(b []byte) (uint8, string) {
- dataSize := len(b)
- if dataSize < 2 || b[0] != 0x16 || b[1] != 0x03 {
+ if len(b) < 2 || b[0] != 0x16 || b[1] != 0x03 {
return acc_proto_tcp, ""
}
rest := b[5:]
@@ -51,27 +50,27 @@ func sniNewParser(b []byte) (uint8, string) {
// Skip over random number
current += 4 + 28
if current >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
// Skip over session ID
sessionIDLength := int(rest[current])
current += 1
current += sessionIDLength
- if current >= restLen {
- return acc_proto_tcp, ""
+ if current+1 >= restLen {
+ return acc_proto_https, ""
}
cipherSuiteLength := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
current += cipherSuiteLength
if current >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
compressionMethodLength := int(rest[current])
current += 1
current += compressionMethodLength
if current >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
current += 2
hostname := ""
@@ -84,27 +83,30 @@ func sniNewParser(b []byte) (uint8, string) {
// Skip over number of names as we're assuming there's just one
current += 2
if current >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
nameType := rest[current]
current += 1
if nameType != 0 {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
if current+1 >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
nameLen := (int(rest[current]) << 8) + int(rest[current+1])
current += 2
if current+nameLen >= restLen {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
}
hostname = string(rest[current : current+nameLen])
}
current += extensionDataLength
}
if hostname == "" {
- return acc_proto_tcp, ""
+ return acc_proto_https, ""
+ }
+ if !validDomainChar(hostname) {
+ return acc_proto_https, ""
}
return acc_proto_https, hostname
}
@@ -150,8 +152,7 @@ func httpNewParser(data []byte) (uint8, string) {
}
func sniParser(data []byte) (uint8, string) {
- dataSize := len(data)
- if dataSize < 2 || data[0] != 0x16 || data[1] != 0x03 {
+ if len(data) < 2 || data[0] != 0x16 || data[1] != 0x03 {
return acc_proto_tcp, ""
}
sniRe := regexp.MustCompile("\x00\x00.{4}\x00.{2}([a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,6})\x00")
@@ -169,3 +170,15 @@ func httpParser(data []byte) (uint8, string) {
}
return acc_proto_tcp, ""
}
+
+// 校验域名的合法字符, 处理乱码问题
+func validDomainChar(addr string) bool {
+ // Allow a-z A-Z . - 0-9
+ for i := 0; i < len(addr); i++ {
+ c := addr[i]
+ if !((c >= 97 && c <= 122) || (c >= 65 && c <= 90) || (c >= 45 && c <= 46) || (c >= 48 && c <= 57)) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/anylink/server/handler/payload_test.go b/anylink/server/handler/payload_test.go
index f36a08c..fc5358b 100644
--- a/anylink/server/handler/payload_test.go
+++ b/anylink/server/handler/payload_test.go
@@ -51,22 +51,26 @@ func BenchmarkNewHttpParser(b *testing.B) {
func TestNewSniParser(t *testing.T) {
ast := assert.New(t)
data := handlerTcpPayload(httpsPacket)
- _, sni := sniNewParser(data)
+ proto, sni := sniNewParser(data)
ast.Equal(sni, httpsSni)
+ ast.Equal(int(proto), acc_proto_https)
}
func TestNewHttpParser(t *testing.T) {
ast := assert.New(t)
// Host
data := handlerTcpPayload(httpPacket)
- _, hostname := httpNewParser(data)
+ proto, hostname := httpNewParser(data)
ast.Equal(hostname, httpHost)
+ ast.Equal(int(proto), acc_proto_http)
// HOST
data = handlerTcpPayload(httpPacket2)
- _, hostname = httpNewParser(data)
+ proto, hostname = httpNewParser(data)
ast.Equal(hostname, httpHost)
+ ast.Equal(int(proto), acc_proto_http)
// GET http://www.google.com/index.html HTTP/1.1
data = handlerTcpPayload(httpPacket3)
- _, hostname = httpNewParser(data)
+ proto, hostname = httpNewParser(data)
ast.Equal(hostname, httpHost)
+ ast.Equal(int(proto), acc_proto_http)
}
diff --git a/anylink/server/handler/server.go b/anylink/server/handler/server.go
index 6cfd4cc..34fd016 100644
--- a/anylink/server/handler/server.go
+++ b/anylink/server/handler/server.go
@@ -3,6 +3,7 @@ package handler
import (
"crypto/tls"
"fmt"
+ "io"
"log"
"net"
"net/http"
@@ -10,8 +11,9 @@ import (
"time"
"github.com/bjdgyc/anylink/base"
- "github.com/bjdgyc/anylink/pkg/proxyproto"
+ "github.com/bjdgyc/anylink/dbdata"
"github.com/gorilla/mux"
+ "github.com/pires/go-proxyproto"
)
func startTls() {
@@ -47,13 +49,19 @@ func startTls() {
NextProtos: []string{"http/1.1"},
MinVersion: tls.VersionTLS12,
CipherSuites: selectedCipherSuites,
+ GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
+ base.Trace("GetCertificate", chi.ServerName)
+ return dbdata.GetCertificateBySNI(chi.ServerName)
+ },
// InsecureSkipVerify: true,
}
srv := &http.Server{
- Addr: addr,
- Handler: initRoute(),
- TLSConfig: tlsConfig,
- ErrorLog: base.GetBaseLog(),
+ Addr: addr,
+ Handler: initRoute(),
+ TLSConfig: tlsConfig,
+ ErrorLog: base.GetBaseLog(),
+ ReadTimeout: 60 * time.Second,
+ WriteTimeout: 60 * time.Second,
}
ln, err = net.Listen("tcp", addr)
@@ -63,11 +71,14 @@ func startTls() {
defer ln.Close()
if base.Cfg.ProxyProtocol {
- ln = &proxyproto.Listener{Listener: ln, ProxyHeaderTimeout: time.Second * 5}
+ ln = &proxyproto.Listener{
+ Listener: ln,
+ ReadHeaderTimeout: 30 * time.Second,
+ }
}
base.Info("listen server", addr)
- err = srv.ServeTLS(ln, base.Cfg.CertFile, base.Cfg.CertKey)
+ err = srv.ServeTLS(ln, "", "")
if err != nil {
base.Fatal(err)
}
@@ -88,6 +99,10 @@ func initRoute() http.Handler {
http.FileServer(http.Dir(base.Cfg.FilesPath)),
),
)
+ // 健康检测
+ r.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "ok")
+ }).Methods(http.MethodGet)
r.NotFoundHandler = http.HandlerFunc(notFound)
return r
}
diff --git a/anylink/server/main.go b/anylink/server/main.go
index c52ddcf..a25efc5 100644
--- a/anylink/server/main.go
+++ b/anylink/server/main.go
@@ -1,5 +1,6 @@
// AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。
+//go:build !windows
// +build !windows
package main
diff --git a/anylink/server/pkg/proxyproto/protocol.go b/anylink/server/pkg/proxyproto/protocol.go
deleted file mode 100644
index f91f0b0..0000000
--- a/anylink/server/pkg/proxyproto/protocol.go
+++ /dev/null
@@ -1,290 +0,0 @@
-// copy from: https://github.com/armon/go-proxyproto/blob/master/protocol.go
-// design: http://www.haproxy.org/download/2.2/doc/proxy-protocol.txt
-
-// HAProxy proxy proto v1
-package proxyproto
-
-import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
- "log"
- "net"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-var (
- // prefix is the string we look for at the start of a connection
- // to check if this connection is using the proxy protocol
- prefix = []byte("PROXY ")
- prefixLen = len(prefix)
-
- ErrInvalidUpstream = errors.New("upstream connection address not trusted for PROXY information")
-)
-
-// SourceChecker can be used to decide whether to trust the PROXY info or pass
-// the original connection address through. If set, the connecting address is
-// passed in as an argument. If the function returns an error due to the source
-// being disallowed, it should return ErrInvalidUpstream.
-//
-// If error is not nil, the call to Accept() will fail. If the reason for
-// triggering this failure is due to a disallowed source, it should return
-// ErrInvalidUpstream.
-//
-// If bool is true, the PROXY-set address is used.
-//
-// If bool is false, the connection's remote address is used, rather than the
-// address claimed in the PROXY info.
-type SourceChecker func(net.Addr) (bool, error)
-
-// Listener is used to wrap an underlying listener,
-// whose connections may be using the HAProxy Proxy Protocol (version 1).
-// If the connection is using the protocol, the RemoteAddr() will return
-// the correct client address.
-//
-// Optionally define ProxyHeaderTimeout to set a maximum time to
-// receive the Proxy Protocol Header. Zero means no timeout.
-type Listener struct {
- Listener net.Listener
- ProxyHeaderTimeout time.Duration
- SourceCheck SourceChecker
- UnknownOK bool // allow PROXY UNKNOWN
-}
-
-// Conn is used to wrap and underlying connection which
-// may be speaking the Proxy Protocol. If it is, the RemoteAddr() will
-// return the address of the client instead of the proxy address.
-type Conn struct {
- bufReader *bufio.Reader
- conn net.Conn
- dstAddr *net.TCPAddr
- srcAddr *net.TCPAddr
- useConnAddr bool
- once sync.Once
- proxyHeaderTimeout time.Duration
- unknownOK bool
-}
-
-// Accept waits for and returns the next connection to the listener.
-func (p *Listener) Accept() (net.Conn, error) {
- // Get the underlying connection
- conn, err := p.Listener.Accept()
- if err != nil {
- return nil, err
- }
- var useConnAddr bool
- if p.SourceCheck != nil {
- allowed, err := p.SourceCheck(conn.RemoteAddr())
- if err != nil {
- return nil, err
- }
- if !allowed {
- useConnAddr = true
- }
- }
- newConn := NewConn(conn, p.ProxyHeaderTimeout)
- newConn.useConnAddr = useConnAddr
- newConn.unknownOK = p.UnknownOK
- return newConn, nil
-}
-
-// Close closes the underlying listener.
-func (p *Listener) Close() error {
- return p.Listener.Close()
-}
-
-// Addr returns the underlying listener's network address.
-func (p *Listener) Addr() net.Addr {
- return p.Listener.Addr()
-}
-
-// NewConn is used to wrap a net.Conn that may be speaking
-// the proxy protocol into a proxyproto.Conn
-func NewConn(conn net.Conn, timeout time.Duration) *Conn {
- pConn := &Conn{
- bufReader: bufio.NewReader(conn),
- conn: conn,
- proxyHeaderTimeout: timeout,
- }
- return pConn
-}
-
-// Read is check for the proxy protocol header when doing
-// the initial scan. If there is an error parsing the header,
-// it is returned and the socket is closed.
-func (p *Conn) Read(b []byte) (int, error) {
- var err error
- p.once.Do(func() { err = p.checkPrefix() })
- if err != nil {
- return 0, err
- }
- return p.bufReader.Read(b)
-}
-
-func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
- if rf, ok := p.conn.(io.ReaderFrom); ok {
- return rf.ReadFrom(r)
- }
- return io.Copy(p.conn, r)
-}
-
-func (p *Conn) WriteTo(w io.Writer) (int64, error) {
- var err error
- p.once.Do(func() { err = p.checkPrefix() })
- if err != nil {
- return 0, err
- }
- return p.bufReader.WriteTo(w)
-}
-
-func (p *Conn) Write(b []byte) (int, error) {
- return p.conn.Write(b)
-}
-
-func (p *Conn) Close() error {
- return p.conn.Close()
-}
-
-func (p *Conn) LocalAddr() net.Addr {
- p.checkPrefixOnce()
- if p.dstAddr != nil && !p.useConnAddr {
- return p.dstAddr
- }
- return p.conn.LocalAddr()
-}
-
-// RemoteAddr returns the address of the client if the proxy
-// protocol is being used, otherwise just returns the address of
-// the socket peer. If there is an error parsing the header, the
-// address of the client is not returned, and the socket is closed.
-// Once implication of this is that the call could block if the
-// client is slow. Using a Deadline is recommended if this is called
-// before Read()
-func (p *Conn) RemoteAddr() net.Addr {
- p.checkPrefixOnce()
- if p.srcAddr != nil && !p.useConnAddr {
- return p.srcAddr
- }
- return p.conn.RemoteAddr()
-}
-
-func (p *Conn) SetDeadline(t time.Time) error {
- return p.conn.SetDeadline(t)
-}
-
-func (p *Conn) SetReadDeadline(t time.Time) error {
- return p.conn.SetReadDeadline(t)
-}
-
-func (p *Conn) SetWriteDeadline(t time.Time) error {
- return p.conn.SetWriteDeadline(t)
-}
-
-func (p *Conn) checkPrefixOnce() {
- p.once.Do(func() {
- if err := p.checkPrefix(); err != nil && err != io.EOF {
- log.Printf("[ERR] Failed to read proxy prefix: %v", err)
- p.Close()
- p.bufReader = bufio.NewReader(p.conn)
- }
- })
-}
-
-func (p *Conn) checkPrefix() error {
- if p.proxyHeaderTimeout != 0 {
- readDeadLine := time.Now().Add(p.proxyHeaderTimeout)
- _ = p.conn.SetReadDeadline(readDeadLine)
- defer func() {
- _ = p.conn.SetReadDeadline(time.Time{})
- }()
- }
-
- // Incrementally check each byte of the prefix
- for i := 1; i <= prefixLen; i++ {
- inp, err := p.bufReader.Peek(i)
-
- if err != nil {
- if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
- return nil
- } else {
- return err
- }
- }
-
- // Check for a prefix mis-match, quit early
- if !bytes.Equal(inp, prefix[:i]) {
- return nil
- }
- }
-
- // Read the header line
- header, err := p.bufReader.ReadString('\n')
- if err != nil {
- p.conn.Close()
- return err
- }
-
- // Strip the carriage return and new line
- header = header[:len(header)-2]
-
- // Split on spaces, should be (PROXY
)
- parts := strings.Split(header, " ")
- if len(parts) < 2 {
- p.conn.Close()
- return fmt.Errorf("Invalid header line: %s", header)
- }
-
- // Verify the type is known
- switch parts[1] {
- case "UNKNOWN":
- if !p.unknownOK || len(parts) != 2 {
- p.conn.Close()
- return fmt.Errorf("Invalid UNKNOWN header line: %s", header)
- }
- p.useConnAddr = true
- return nil
- case "TCP4":
- case "TCP6":
- default:
- p.conn.Close()
- return fmt.Errorf("Unhandled address type: %s", parts[1])
- }
-
- if len(parts) != 6 {
- p.conn.Close()
- return fmt.Errorf("Invalid header line: %s", header)
- }
-
- // Parse out the source address
- ip := net.ParseIP(parts[2])
- if ip == nil {
- p.conn.Close()
- return fmt.Errorf("Invalid source ip: %s", parts[2])
- }
- port, err := strconv.Atoi(parts[4])
- if err != nil {
- p.conn.Close()
- return fmt.Errorf("Invalid source port: %s", parts[4])
- }
- p.srcAddr = &net.TCPAddr{IP: ip, Port: port}
-
- // Parse out the destination address
- ip = net.ParseIP(parts[3])
- if ip == nil {
- p.conn.Close()
- return fmt.Errorf("Invalid destination ip: %s", parts[3])
- }
- port, err = strconv.Atoi(parts[5])
- if err != nil {
- p.conn.Close()
- return fmt.Errorf("Invalid destination port: %s", parts[5])
- }
- p.dstAddr = &net.TCPAddr{IP: ip, Port: port}
-
- return nil
-}
diff --git a/anylink/server/pkg/proxyproto/protocol_test.go b/anylink/server/pkg/proxyproto/protocol_test.go
deleted file mode 100644
index 1ad37aa..0000000
--- a/anylink/server/pkg/proxyproto/protocol_test.go
+++ /dev/null
@@ -1,486 +0,0 @@
-// copy from: https://github.com/armon/go-proxyproto/blob/master/protocol_test.go
-package proxyproto
-
-import (
- "bytes"
- "io"
- "net"
- "testing"
- "time"
-)
-
-const (
- goodAddr = "127.0.0.1"
- badAddr = "127.0.0.2"
- errAddr = "9999.0.0.2"
-)
-
-var (
- checkAddr string
-)
-
-func TestPassthrough(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- pl := &Listener{Listener: l}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-}
-
-func TestTimeout(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- clientWriteDelay := 200 * time.Millisecond
- proxyHeaderTimeout := 50 * time.Millisecond
- pl := &Listener{Listener: l, ProxyHeaderTimeout: proxyHeaderTimeout}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Do not send data for a while
- time.Sleep(clientWriteDelay)
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Check the remote addr is the original 127.0.0.1
- remoteAddrStartTime := time.Now()
- addr := conn.RemoteAddr().(*net.TCPAddr)
- if addr.IP.String() != "127.0.0.1" {
- t.Fatalf("bad: %v", addr)
- }
- remoteAddrDuration := time.Since(remoteAddrStartTime)
-
- // Check RemoteAddr() call did timeout
- if remoteAddrDuration >= clientWriteDelay {
- t.Fatalf("RemoteAddr() took longer than the specified timeout: %v < %v", proxyHeaderTimeout, remoteAddrDuration)
- }
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-}
-
-func TestParse_ipv4(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- pl := &Listener{Listener: l}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Write out the header!
- header := "PROXY TCP4 10.1.1.1 20.2.2.2 1000 2000\r\n"
- conn.Write([]byte(header))
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Check the remote addr
- addr := conn.RemoteAddr().(*net.TCPAddr)
- if addr.IP.String() != "10.1.1.1" {
- t.Fatalf("bad: %v", addr)
- }
- if addr.Port != 1000 {
- t.Fatalf("bad: %v", addr)
- }
-}
-
-func TestParse_ipv6(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- pl := &Listener{Listener: l}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Write out the header!
- header := "PROXY TCP6 ffff::ffff ffff::ffff 1000 2000\r\n"
- conn.Write([]byte(header))
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Check the remote addr
- addr := conn.RemoteAddr().(*net.TCPAddr)
- if addr.IP.String() != "ffff::ffff" {
- t.Fatalf("bad: %v", addr)
- }
- if addr.Port != 1000 {
- t.Fatalf("bad: %v", addr)
- }
-}
-
-func TestParse_Unknown(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- pl := &Listener{Listener: l, UnknownOK: true}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Write out the header!
- header := "PROXY UNKNOWN\r\n"
- conn.Write([]byte(header))
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
-}
-
-func TestParse_BadHeader(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- pl := &Listener{Listener: l}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Write out the header!
- header := "PROXY TCP4 what 127.0.0.1 1000 2000\r\n"
- conn.Write([]byte(header))
-
- conn.Write([]byte("ping"))
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err == nil {
- t.Fatalf("err: %v", err)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Check the remote addr, should be the local addr
- addr := conn.RemoteAddr().(*net.TCPAddr)
- if addr.IP.String() != "127.0.0.1" {
- t.Fatalf("bad: %v", addr)
- }
-
- // Read should fail
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err == nil {
- t.Fatalf("err: %v", err)
- }
-}
-
-func TestParse_ipv4_checkfunc(t *testing.T) {
- checkAddr = goodAddr
- testParse_ipv4_checkfunc(t)
- checkAddr = badAddr
- testParse_ipv4_checkfunc(t)
- checkAddr = errAddr
- testParse_ipv4_checkfunc(t)
-}
-
-func testParse_ipv4_checkfunc(t *testing.T) {
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- checkFunc := func(addr net.Addr) (bool, error) {
- tcpAddr := addr.(*net.TCPAddr)
- if tcpAddr.IP.String() == checkAddr {
- return true, nil
- }
- return false, nil
- }
-
- pl := &Listener{Listener: l, SourceCheck: checkFunc}
-
- go func() {
- conn, err := net.Dial("tcp", pl.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- // Write out the header!
- header := "PROXY TCP4 10.1.1.1 20.2.2.2 1000 2000\r\n"
- conn.Write([]byte(header))
-
- conn.Write([]byte("ping"))
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("pong")) {
- t.Fatalf("bad: %v", recv)
- }
- }()
-
- conn, err := pl.Accept()
- if err != nil {
- if checkAddr == badAddr {
- return
- }
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- recv := make([]byte, 4)
- _, err = conn.Read(recv)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- if !bytes.Equal(recv, []byte("ping")) {
- t.Fatalf("bad: %v", recv)
- }
-
- if _, err := conn.Write([]byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Check the remote addr
- addr := conn.RemoteAddr().(*net.TCPAddr)
- switch checkAddr {
- case goodAddr:
- if addr.IP.String() != "10.1.1.1" {
- t.Fatalf("bad: %v", addr)
- }
- if addr.Port != 1000 {
- t.Fatalf("bad: %v", addr)
- }
- case badAddr:
- if addr.IP.String() != "127.0.0.1" {
- t.Fatalf("bad: %v", addr)
- }
- if addr.Port == 1000 {
- t.Fatalf("bad: %v", addr)
- }
- }
-}
-
-type testConn struct {
- readFromCalledWith io.Reader
- net.Conn // nil; crash on any unexpected use
-}
-
-func (c *testConn) ReadFrom(r io.Reader) (int64, error) {
- c.readFromCalledWith = r
- return 0, nil
-}
-func (c *testConn) Write(p []byte) (int, error) {
- return len(p), nil
-}
-func (c *testConn) Read(p []byte) (int, error) {
- return 1, nil
-}
-
-func TestCopyToWrappedConnection(t *testing.T) {
- innerConn := &testConn{}
- wrappedConn := NewConn(innerConn, 0)
- dummySrc := &testConn{}
-
- io.Copy(wrappedConn, dummySrc)
- if innerConn.readFromCalledWith != dummySrc {
- t.Error("Expected io.Copy to delegate to ReadFrom function of inner destination connection")
- }
-}
-
-func TestCopyFromWrappedConnection(t *testing.T) {
- wrappedConn := NewConn(&testConn{}, 0)
- dummyDst := &testConn{}
-
- io.Copy(dummyDst, wrappedConn)
- if dummyDst.readFromCalledWith != wrappedConn.conn {
- t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom method of destination")
- }
-}
-
-func TestCopyFromWrappedConnectionToWrappedConnection(t *testing.T) {
- innerConn1 := &testConn{}
- wrappedConn1 := NewConn(innerConn1, 0)
- innerConn2 := &testConn{}
- wrappedConn2 := NewConn(innerConn2, 0)
-
- io.Copy(wrappedConn1, wrappedConn2)
- if innerConn1.readFromCalledWith != innerConn2 {
- t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom of inner destination connection")
- }
-
-}
diff --git a/anylink/server/sessdata/compress.go b/anylink/server/sessdata/compress.go
new file mode 100644
index 0000000..7156f89
--- /dev/null
+++ b/anylink/server/sessdata/compress.go
@@ -0,0 +1,35 @@
+package sessdata
+
+import (
+ "github.com/lanrenwo/lzsgo"
+)
+
+type CmpEncoding interface {
+ Compress(src []byte, dst []byte) (int, error)
+ Uncompress(src []byte, dst []byte) (int, error)
+}
+
+type LzsgoCmp struct {
+}
+
+func (l LzsgoCmp) Compress(src []byte, dst []byte) (int, error) {
+ n, err := lzsgo.Compress(src, dst)
+ return n, err
+}
+
+func (l LzsgoCmp) Uncompress(src []byte, dst []byte) (int, error) {
+ n, err := lzsgo.Uncompress(src, dst)
+ return n, err
+}
+
+// type Lz4Cmp struct {
+// c lz4.Compressor
+// }
+
+// func (l Lz4Cmp) Compress(src []byte, dst []byte) (int, error) {
+// return l.c.CompressBlock(src, dst)
+// }
+
+// func (l Lz4Cmp) Uncompress(src []byte, dst []byte) (int, error) {
+// return lz4.UncompressBlock(src, dst)
+// }
diff --git a/anylink/server/sessdata/compress_test.go b/anylink/server/sessdata/compress_test.go
new file mode 100644
index 0000000..ce3a317
--- /dev/null
+++ b/anylink/server/sessdata/compress_test.go
@@ -0,0 +1,28 @@
+package sessdata
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLzsCompress(t *testing.T) {
+ var (
+ n int
+ err error
+ )
+ assert := assert.New(t)
+ c := LzsgoCmp{}
+ s := "hello anylink, you are best!"
+ src := []byte(strings.Repeat(s, 50))
+
+ comprBuf := make([]byte, 2048)
+ n, err = c.Compress(src, comprBuf)
+ assert.Nil(err)
+
+ unprBuf := make([]byte, 2048)
+ n, err = c.Uncompress(comprBuf[:n], unprBuf)
+ assert.Nil(err)
+ assert.Equal(src, unprBuf[:n])
+}
diff --git a/anylink/server/sessdata/ip_pool.go b/anylink/server/sessdata/ip_pool.go
index 1ab5ae2..41dc773 100644
--- a/anylink/server/sessdata/ip_pool.go
+++ b/anylink/server/sessdata/ip_pool.go
@@ -14,8 +14,10 @@ var (
IpPool = &ipPoolConfig{}
ipActive = map[string]bool{}
// ipKeep and ipLease ipAddr => type
- ipLease = map[string]bool{}
+ // ipLease = map[string]bool{}
ipPoolMux sync.Mutex
+ // 记录循环点
+ loopCurIp uint32
)
type ipPoolConfig struct {
@@ -36,7 +38,19 @@ func initIpPool() {
}
IpPool.Ipv4IPNet = ipNet
IpPool.Ipv4Mask = net.IP(ipNet.Mask)
- IpPool.Ipv4Gateway = net.ParseIP(base.Cfg.Ipv4Gateway)
+
+ ipv4Gateway := net.ParseIP(base.Cfg.Ipv4Gateway)
+ ipStart := net.ParseIP(base.Cfg.Ipv4Start)
+ ipEnd := net.ParseIP(base.Cfg.Ipv4End)
+ if !ipNet.Contains(ipv4Gateway) || !ipNet.Contains(ipStart) || !ipNet.Contains(ipEnd) {
+ panic("ip段 设置错误")
+ }
+ // ip地址池
+ IpPool.Ipv4Gateway = ipv4Gateway
+ IpPool.IpLongMin = utils.Ip2long(ipStart)
+ IpPool.IpLongMax = utils.Ip2long(ipEnd)
+
+ loopCurIp = IpPool.IpLongMin
// 网络地址零值
// zero := binary.BigEndian.Uint32(ip.Mask(mask))
@@ -44,71 +58,160 @@ func initIpPool() {
// one, _ := ipNet.Mask.Size()
// max := min | uint32(math.Pow(2, float64(32-one))-1)
- // ip地址池
- IpPool.IpLongMin = utils.Ip2long(net.ParseIP(base.Cfg.Ipv4Start))
- IpPool.IpLongMax = utils.Ip2long(net.ParseIP(base.Cfg.Ipv4End))
-
// 获取IpLease数据
- go cronIpLease()
+ // go cronIpLease()
}
-func cronIpLease() {
- getIpLease()
- tick := time.NewTicker(time.Minute * 30)
- for range tick.C {
- getIpLease()
- }
-}
-
-func getIpLease() {
- xdb := dbdata.GetXdb()
- keepIpMaps := []dbdata.IpMap{}
- sNow := time.Now().Add(-1 * time.Duration(base.Cfg.IpLease) * time.Second)
- err := xdb.Cols("ip_addr").Where("keep=?", true).Or("last_login>?", sNow).Find(&keepIpMaps)
- if err != nil {
- base.Error(err)
- }
- // fmt.Println(keepIpMaps)
- ipPoolMux.Lock()
- ipLease = map[string]bool{}
- for _, v := range keepIpMaps {
- ipLease[v.IpAddr] = true
- }
- ipPoolMux.Unlock()
-}
+// func cronIpLease() {
+// getIpLease()
+// tick := time.NewTicker(time.Minute * 30)
+// for range tick.C {
+// getIpLease()
+// }
+// }
+//
+// func getIpLease() {
+// xdb := dbdata.GetXdb()
+// keepIpMaps := []dbdata.IpMap{}
+// sNow := time.Now().Add(-1 * time.Duration(base.Cfg.IpLease) * time.Second)
+// err := xdb.Cols("ip_addr").Where("keep=?", true).
+// Or("unique_mac=? and last_login>?", true, sNow).Find(&keepIpMaps)
+// if err != nil {
+// base.Error(err)
+// }
+// // fmt.Println(keepIpMaps)
+// ipPoolMux.Lock()
+// ipLease = map[string]bool{}
+// for _, v := range keepIpMaps {
+// ipLease[v.IpAddr] = true
+// }
+// ipPoolMux.Unlock()
+// }
// AcquireIp 获取动态ip
-func AcquireIp(username, macAddr string) net.IP {
+func AcquireIp(username, macAddr string, uniqueMac bool) net.IP {
+ base.Trace("AcquireIp:", username, macAddr, uniqueMac)
ipPoolMux.Lock()
defer ipPoolMux.Unlock()
- tNow := time.Now()
+ var (
+ err error
+ tNow = time.Now()
+ )
- // 判断是否已经分配过
- mi := &dbdata.IpMap{}
- err := dbdata.One("mac_addr", macAddr, mi)
- // 存在ip记录
- if err == nil {
+ if uniqueMac {
+ // 判断是否已经分配过
+ mi := &dbdata.IpMap{}
+ err = dbdata.One("mac_addr", macAddr, mi)
+ if err != nil {
+ // 没有查询到数据
+ if dbdata.CheckErrNotFound(err) {
+ return loopIp(username, macAddr, uniqueMac)
+ }
+ // 查询报错
+ base.Error(err)
+ return nil
+ }
+
+ // 存在ip记录
+ base.Trace("uniqueMac:", username, mi)
ipStr := mi.IpAddr
ip := net.ParseIP(ipStr)
// 跳过活跃连接
_, ok := ipActive[ipStr]
// 检测原有ip是否在新的ip池内
- if IpPool.Ipv4IPNet.Contains(ip) && !ok &&
+ // IpPool.Ipv4IPNet.Contains(ip) &&
+ if !ok &&
utils.Ip2long(ip) >= IpPool.IpLongMin &&
utils.Ip2long(ip) <= IpPool.IpLongMax {
mi.Username = username
mi.LastLogin = tNow
+ mi.UniqueMac = uniqueMac
// 回写db数据
_ = dbdata.Set(mi)
ipActive[ipStr] = true
return ip
}
+ // 删除当前macAddr
+ mi = &dbdata.IpMap{MacAddr: macAddr}
_ = dbdata.Del(mi)
+
+ } else {
+ // 没有获取到mac的情况
+ ipMaps := []dbdata.IpMap{}
+ err = dbdata.FindWhere(&ipMaps, 50, 1, "username=? and unique_mac=?", username, false)
+ if err != nil {
+ // 没有查询到数据
+ if dbdata.CheckErrNotFound(err) {
+ return loopIp(username, macAddr, uniqueMac)
+ }
+ // 查询报错
+ base.Error(err)
+ return nil
+ }
+
+ // 遍历mac记录
+ for _, mi := range ipMaps {
+ ipStr := mi.IpAddr
+ ip := net.ParseIP(ipStr)
+
+ // 跳过活跃连接
+ if _, ok := ipActive[ipStr]; ok {
+ continue
+ }
+ // 跳过保留ip
+ if mi.Keep {
+ continue
+ }
+ // 没有mac的 不需要验证租期
+ // mi.LastLogin.Before(leaseTime) &&
+ if utils.Ip2long(ip) >= IpPool.IpLongMin &&
+ utils.Ip2long(ip) <= IpPool.IpLongMax {
+ mi.LastLogin = tNow
+ mi.MacAddr = macAddr
+ mi.UniqueMac = uniqueMac
+ // 回写db数据
+ _ = dbdata.Set(mi)
+ ipActive[ipStr] = true
+ return ip
+ }
+ }
}
+ return loopIp(username, macAddr, uniqueMac)
+}
+
+func loopIp(username, macAddr string, uniqueMac bool) net.IP {
+ var (
+ i uint32
+ ip net.IP
+ )
+
+ i, ip = loopLong(loopCurIp, IpPool.IpLongMax, username, macAddr, uniqueMac)
+ if ip != nil {
+ loopCurIp = i
+ return ip
+ }
+
+ i, ip = loopLong(IpPool.IpLongMin, loopCurIp, username, macAddr, uniqueMac)
+ if ip != nil {
+ loopCurIp = i
+ return ip
+ }
+
+ base.Warn("no ip available, please see ip_map table row", username, macAddr)
+ return nil
+}
+
+func loopLong(start, end uint32, username, macAddr string, uniqueMac bool) (uint32, net.IP) {
+ var (
+ err error
+ tNow = time.Now()
+ leaseTime = time.Now().Add(-1 * time.Duration(base.Cfg.IpLease) * time.Second)
+ )
+
// 全局遍历超过租期和未保留的ip
- for i := IpPool.IpLongMin; i <= IpPool.IpLongMax; i++ {
+ for i := start; i <= end; i++ {
ip := utils.Long2ip(i)
ipStr := ip.String()
@@ -116,32 +219,42 @@ func AcquireIp(username, macAddr string) net.IP {
if _, ok := ipActive[ipStr]; ok {
continue
}
- // 跳过ip租期内数据
- if _, ok := ipLease[ipStr]; ok {
- continue
+
+ mi := &dbdata.IpMap{}
+ err = dbdata.One("ip_addr", ipStr, mi)
+ if err != nil {
+ // 没有查询到数据
+ if dbdata.CheckErrNotFound(err) {
+ // 该ip没有被使用
+ mi = &dbdata.IpMap{IpAddr: ipStr, MacAddr: macAddr, UniqueMac: uniqueMac, Username: username, LastLogin: tNow}
+ _ = dbdata.Add(mi)
+ ipActive[ipStr] = true
+ return i, ip
+ }
+ // 查询报错
+ base.Error(err)
+ return 0, nil
}
- v := &dbdata.IpMap{}
- err = dbdata.One("ip_addr", ipStr, v)
- if err == nil {
- // 存在记录直接跳过
+ // 查询到已经使用的ip
+ // 跳过保留ip
+ if mi.Keep {
continue
}
-
- if dbdata.CheckErrNotFound(err) {
- // 该ip没有被使用
- mi = &dbdata.IpMap{IpAddr: ipStr, MacAddr: macAddr, Username: username, LastLogin: tNow}
- _ = dbdata.Add(mi)
+ // 判断租期
+ if mi.LastLogin.Before(leaseTime) {
+ // 存在记录,说明已经超过租期,可以直接使用
+ mi.LastLogin = tNow
+ mi.MacAddr = macAddr
+ mi.UniqueMac = uniqueMac
+ // 回写db数据
+ _ = dbdata.Set(mi)
ipActive[ipStr] = true
- return ip
+ return i, ip
}
- // 查询报错
- base.Error(err)
- return nil
}
- base.Warn("no ip available, please see ip_map table row")
- return nil
+ return 0, nil
}
// 回收ip
diff --git a/anylink/server/sessdata/ip_pool_test.go b/anylink/server/sessdata/ip_pool_test.go
index 5db01cd..d103112 100644
--- a/anylink/server/sessdata/ip_pool_test.go
+++ b/anylink/server/sessdata/ip_pool_test.go
@@ -6,6 +6,7 @@ import (
"os"
"path"
"testing"
+ "time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
@@ -18,10 +19,12 @@ func preData(tmpDir string) {
base.Cfg.DbType = "sqlite3"
base.Cfg.DbSource = tmpDb
base.Cfg.Ipv4CIDR = "192.168.3.0/24"
- base.Cfg.Ipv4Start = "192.168.3.1"
- base.Cfg.Ipv4End = "192.168.3.199"
+ base.Cfg.Ipv4Gateway = "192.168.3.1"
+ base.Cfg.Ipv4Start = "192.168.3.100"
+ base.Cfg.Ipv4End = "192.168.3.150"
base.Cfg.MaxClient = 100
base.Cfg.MaxUserClient = 3
+ base.Cfg.IpLease = 5
dbdata.Start()
group := dbdata.Group{
@@ -46,22 +49,34 @@ func TestIpPool(t *testing.T) {
var ip net.IP
- for i := 1; i <= 100; i++ {
- _ = AcquireIp("user", fmt.Sprintf("mac-%d", i))
+ for i := 100; i <= 150; i++ {
+ _ = AcquireIp(getTestUser(i), getTestMacAddr(i), true)
}
- ip = AcquireIp("user", "mac-new")
- assert.True(net.IPv4(192, 168, 3, 101).Equal(ip))
- for i := 102; i <= 199; i++ {
- ip = AcquireIp("user", fmt.Sprintf("mac-%d", i))
- }
- assert.True(net.IPv4(192, 168, 3, 199).Equal(ip))
- ip = AcquireIp("user", "mac-nil")
- assert.Nil(ip)
- ReleaseIp(net.IPv4(192, 168, 3, 88), "mac-88")
- ReleaseIp(net.IPv4(192, 168, 3, 188), "mac-188")
+ // 回收
+ ReleaseIp(net.IPv4(192, 168, 3, 140), getTestMacAddr(140))
+ time.Sleep(time.Second * 6)
+
// 从头循环获取可用ip
- ip = AcquireIp("user", "mac-188")
- t.Log("mac-188", ip)
- assert.True(net.IPv4(192, 168, 3, 188).Equal(ip))
+ user_new := getTestUser(210)
+ mac_new := getTestMacAddr(210)
+ ip = AcquireIp(user_new, mac_new, true)
+ t.Log("mac_new", ip)
+ assert.NotNil(ip)
+ assert.True(net.IPv4(192, 168, 3, 140).Equal(ip))
+
+ // 回收全部
+ for i := 100; i <= 150; i++ {
+ ReleaseIp(net.IPv4(192, 168, 3, byte(i)), getTestMacAddr(i))
+ }
+}
+
+func getTestUser(i int) string {
+ return fmt.Sprintf("user-%d", i)
+}
+
+func getTestMacAddr(i int) string {
+ // 前缀mac
+ macAddr := "02:00:00:00:00"
+ return fmt.Sprintf("%s:%x", macAddr, i)
}
diff --git a/anylink/server/sessdata/online.go b/anylink/server/sessdata/online.go
index e8dff7a..7ac526d 100644
--- a/anylink/server/sessdata/online.go
+++ b/anylink/server/sessdata/online.go
@@ -14,6 +14,7 @@ type Online struct {
Username string `json:"username"`
Group string `json:"group"`
MacAddr string `json:"mac_addr"`
+ UniqueMac bool `json:"unique_mac"`
Ip net.IP `json:"ip"`
RemoteAddr string `json:"remote_addr"`
TunName string `json:"tun_name"`
@@ -52,6 +53,7 @@ func OnlineSess() []Online {
Username: v.Username,
Group: v.Group,
MacAddr: v.MacAddr,
+ UniqueMac: v.UniqueMac,
RemoteAddr: v.CSess.RemoteAddr,
TunName: v.CSess.IfName,
Mtu: v.CSess.Mtu,
diff --git a/anylink/server/sessdata/session.go b/anylink/server/sessdata/session.go
index 70f3e7e..fc38928 100644
--- a/anylink/server/sessdata/session.go
+++ b/anylink/server/sessdata/session.go
@@ -12,8 +12,7 @@ import (
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
- "github.com/bjdgyc/anylink/pkg/utils"
- "github.com/ivpusic/grpool"
+ mapset "github.com/deckarep/golang-set"
atomic2 "go.uber.org/atomic"
)
@@ -37,6 +36,8 @@ type ConnSession struct {
Mtu int
IfName string
Client string // 客户端 mobile pc
+ UserAgent string // 客户端信息
+ UserLogoutCode uint8 // 用户/客户端主动登出
CstpDpd int
Group *dbdata.Group
Limit *LimitRater
@@ -51,10 +52,11 @@ type ConnSession struct {
PayloadIn chan *Payload
PayloadOutCstp chan *Payload // Cstp的数据
PayloadOutDtls chan *Payload // Dtls的数据
- IpAuditMap utils.IMaps // 审计的ip数据
- IpAuditPool *grpool.Pool // 审计的IP包解析池
// dSess *DtlsSession
dSess *atomic.Value
+ // compress
+ CstpPickCmp CmpEncoding
+ DtlsPickCmp CmpEncoding
}
type DtlsSession struct {
@@ -65,17 +67,22 @@ type DtlsSession struct {
}
type Session struct {
- mux sync.RWMutex
- Sid string // auth返回的 session-id
- Token string // session信息的唯一token
- DtlsSid string // dtls协议的 session_id
- MacAddr string // 客户端mac地址
- UniqueIdGlobal string // 客户端唯一标示
- MacHw net.HardwareAddr
- Username string // 用户名
- Group string
- AuthStep string
- AuthPass string
+ mux sync.RWMutex
+ Sid string // auth返回的 session-id
+ Token string // session信息的唯一token
+ DtlsSid string // dtls协议的 session_id
+ MacAddr string // 客户端mac地址
+ UniqueIdGlobal string // 客户端唯一标示
+ MacHw net.HardwareAddr
+ UniqueMac bool // 客户端获取到真实设备mac
+ Username string // 用户名
+ Group string
+ AuthStep string
+ AuthPass string
+ RemoteAddr string
+ UserAgent string
+ DeviceType string
+ PlatformVersion string
LastLogin time.Time
IsActive bool
@@ -97,22 +104,46 @@ func checkSession() {
timeout := time.Duration(base.Cfg.SessionTimeout) * time.Second
tick := time.NewTicker(time.Second * 60)
for range tick.C {
- sessMux.Lock()
+ outToken := []string{}
+ sessMux.RLock()
t := time.Now()
for k, v := range sessions {
- v.mux.Lock()
+ v.mux.RLock()
if !v.IsActive {
if t.Sub(v.LastLogin) > timeout {
- delete(sessions, k)
+ outToken = append(outToken, k)
}
}
- v.mux.Unlock()
+ v.mux.RUnlock()
+ }
+ sessMux.RUnlock()
+
+ // 删除过期session
+ for _, v := range outToken {
+ CloseSess(v, dbdata.UserLogoutTimeout)
}
- sessMux.Unlock()
}
}()
}
+// 状态为过期的用户踢下线
+func CloseUserLimittimeSession() {
+ s := mapset.NewSetFromSlice(dbdata.CheckUserlimittime())
+ limitTimeToken := []string{}
+ sessMux.RLock()
+ for _, v := range sessions {
+ v.mux.RLock()
+ if v.IsActive && s.Contains(v.Username) {
+ limitTimeToken = append(limitTimeToken, v.Token)
+ }
+ v.mux.RUnlock()
+ }
+ sessMux.RUnlock()
+ for _, v := range limitTimeToken {
+ CloseSess(v, dbdata.UserLogoutExpire)
+ }
+}
+
func GenToken() string {
// 生成32位的 token
bToken := make([]byte, 32)
@@ -151,6 +182,7 @@ func (s *Session) NewConn() *ConnSession {
macAddr := s.MacAddr
macHw := s.MacHw
username := s.Username
+ uniqueMac := s.UniqueMac
s.mux.RUnlock()
if active {
s.CSess.Close()
@@ -158,9 +190,10 @@ func (s *Session) NewConn() *ConnSession {
limit := LimitClient(username, false)
if !limit {
+ base.Warn("limit is full", username)
return nil
}
- ip := AcquireIp(username, macAddr)
+ ip := AcquireIp(username, macAddr, uniqueMac)
if ip == nil {
LimitClient(username, true)
return nil
@@ -187,12 +220,6 @@ func (s *Session) NewConn() *ConnSession {
dSess: &atomic.Value{},
}
- // ip 审计
- if base.Cfg.AuditInterval >= 0 {
- cSess.IpAuditMap = utils.NewMap("cmap", 0)
- cSess.IpAuditPool = grpool.NewPool(1, 600)
- }
-
dSess := &DtlsSession{
isActive: -1,
}
@@ -232,6 +259,7 @@ func (cs *ConnSession) Close() {
ReleaseIp(cs.IpAddr, cs.Sess.MacAddr)
LimitClient(cs.Username, true)
+ AddUserActLog(cs)
})
}
@@ -335,6 +363,30 @@ func (cs *ConnSession) RateLimit(byt int, isUp bool) error {
return cs.Limit.Wait(byt)
}
+func (cs *ConnSession) SetPickCmp(cate, encoding string) (string, bool) {
+ var cmpName string
+ if !base.Cfg.Compression {
+ return cmpName, false
+ }
+ var cmp CmpEncoding
+ switch {
+ // case strings.Contains(encoding, "oc-lz4"):
+ // cmpName = "oc-lz4"
+ // cmp = Lz4Cmp{}
+ case strings.Contains(encoding, "lzs"):
+ cmpName = "lzs"
+ cmp = LzsgoCmp{}
+ default:
+ return cmpName, false
+ }
+ if cate == "cstp" {
+ cs.CstpPickCmp = cmp
+ } else {
+ cs.DtlsPickCmp = cmp
+ }
+ return cmpName, true
+}
+
func SToken2Sess(stoken string) *Session {
stoken = strings.TrimSpace(stoken)
sarr := strings.Split(stoken, "@")
@@ -356,6 +408,20 @@ func Dtls2Sess(did string) *Session {
return sessions[token]
}
+func Dtls2CSess(did string) *ConnSession {
+ sessMux.RLock()
+ defer sessMux.RUnlock()
+ token := dtlsIds[did]
+ sess := sessions[token]
+ if sess == nil {
+ return nil
+ }
+
+ sess.mux.RLock()
+ defer sess.mux.RUnlock()
+ return sess.CSess
+}
+
func Dtls2MasterSecret(did string) string {
sessMux.RLock()
token := dtlsIds[did]
@@ -378,7 +444,7 @@ func DelSess(token string) {
// sessions.Delete(token)
}
-func CloseSess(token string) {
+func CloseSess(token string, code ...uint8) {
sessMux.Lock()
defer sessMux.Unlock()
sess, ok := sessions[token]
@@ -387,7 +453,16 @@ func CloseSess(token string) {
}
delete(sessions, token)
- sess.CSess.Close()
+ delete(dtlsIds, sess.DtlsSid)
+
+ if sess.CSess != nil {
+ if len(code) > 0 {
+ sess.CSess.UserLogoutCode = code[0]
+ }
+ sess.CSess.Close()
+ return
+ }
+ AddUserActLogBySess(sess)
}
func CloseCSess(token string) {
@@ -398,14 +473,42 @@ func CloseCSess(token string) {
return
}
- sess.CSess.Close()
+ if sess.CSess != nil {
+ sess.CSess.Close()
+ }
}
func DelSessByStoken(stoken string) {
stoken = strings.TrimSpace(stoken)
sarr := strings.Split(stoken, "@")
token := sarr[1]
- sessMux.Lock()
- delete(sessions, token)
- sessMux.Unlock()
+ CloseSess(token, dbdata.UserLogoutBanner)
+}
+
+func AddUserActLog(cs *ConnSession) {
+ ua := dbdata.UserActLog{
+ Username: cs.Sess.Username,
+ GroupName: cs.Sess.Group,
+ IpAddr: cs.IpAddr.String(),
+ RemoteAddr: cs.RemoteAddr,
+ DeviceType: cs.Sess.DeviceType,
+ PlatformVersion: cs.Sess.PlatformVersion,
+ Status: dbdata.UserLogout,
+ }
+ ua.Info = dbdata.UserActLogIns.GetInfoOpsById(cs.UserLogoutCode)
+ dbdata.UserActLogIns.Add(ua, cs.UserAgent)
+}
+
+func AddUserActLogBySess(sess *Session) {
+ ua := dbdata.UserActLog{
+ Username: sess.Username,
+ GroupName: sess.Group,
+ IpAddr: "",
+ RemoteAddr: sess.RemoteAddr,
+ DeviceType: sess.DeviceType,
+ PlatformVersion: sess.PlatformVersion,
+ Status: dbdata.UserLogout,
+ }
+ ua.Info = dbdata.UserActLogIns.GetInfoOpsById(dbdata.UserLogoutBanner)
+ dbdata.UserActLogIns.Add(ua, sess.UserAgent)
}
diff --git a/anylink/server/sessdata/session_test.go b/anylink/server/sessdata/session_test.go
index c9219b2..6719f61 100644
--- a/anylink/server/sessdata/session_test.go
+++ b/anylink/server/sessdata/session_test.go
@@ -1,8 +1,11 @@
package sessdata
import (
+ "fmt"
"testing"
+ "time"
+ "github.com/bjdgyc/anylink/base"
"github.com/stretchr/testify/assert"
)
@@ -22,11 +25,15 @@ func TestConnSession(t *testing.T) {
preData(tmp)
defer cleardata(tmp)
+ time.Sleep(time.Second * 10)
+
sess := NewSession("")
+ sess.Username = "user-test"
sess.Group = "group1"
sess.MacAddr = "00:15:5d:50:14:43"
cSess := sess.NewConn()
+ base.Info("cSess", cSess)
err := cSess.RateLimit(100, true)
ast.Nil(err)
@@ -34,5 +41,23 @@ func TestConnSession(t *testing.T) {
err = cSess.RateLimit(200, false)
ast.Nil(err)
ast.Equal(cSess.BandwidthDown.Load(), uint32(200))
+
+ var (
+ cmpName string
+ ok bool
+ )
+ base.Cfg.Compression = true
+
+ cmpName, ok = cSess.SetPickCmp("cstp", "oc-lz4,lzs")
+ fmt.Println(cmpName, ok)
+ ast.True(ok)
+ ast.Equal(cmpName, "lzs")
+ cmpName, ok = cSess.SetPickCmp("dtls", "lzs")
+ ast.True(ok)
+ ast.Equal(cmpName, "lzs")
+ cmpName, ok = cSess.SetPickCmp("dtls", "test")
+ ast.False(ok)
+ ast.Equal(cmpName, "")
+
cSess.Close()
}
diff --git a/anylink/server/sessdata/start.go b/anylink/server/sessdata/start.go
index 1d4243c..7862574 100644
--- a/anylink/server/sessdata/start.go
+++ b/anylink/server/sessdata/start.go
@@ -4,4 +4,5 @@ func Start() {
initIpPool()
checkSession()
saveStatsInfo()
+ CloseUserLimittimeSession()
}
diff --git a/anylink/systemd/anylink.service b/anylink/systemd/anylink.service
index 9f1fe68..72c3297 100644
--- a/anylink/systemd/anylink.service
+++ b/anylink/systemd/anylink.service
@@ -11,5 +11,12 @@ Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/anylink-deploy/anylink --conf=/usr/local/anylink-deploy/conf/server.toml
+# systemd older than v236
+# ExecStart=/bin/bash -c 'exec /usr/local/anylink-deploy/anylink --conf=/usr/local/anylink-deploy/conf/server.toml >> /usr/local/anylink-deploy/log/anylink.log 2>&1'
+
+
+StandardOutput=file:/usr/local/anylink-deploy/log/anylink.log
+StandardError=file:/usr/local/anylink-deploy/log/anylink.log
+
[Install]
WantedBy=multi-user.target
diff --git a/anylink/web/package.json b/anylink/web/package.json
index 11ad5f9..73983b4 100644
--- a/anylink/web/package.json
+++ b/anylink/web/package.json
@@ -12,6 +12,7 @@
"core-js": "^3.6.5",
"echarts": "^4.9.0",
"element-ui": "^2.4.5",
+ "qs": "^6.11.1",
"vue": "^2.6.11",
"vue-count-to": "^1.0.13",
"vue-router": "^3.5.2"
diff --git a/anylink/web/public/批量添加用户模版.xlsx b/anylink/web/public/批量添加用户模版.xlsx
new file mode 100644
index 0000000..5405de7
Binary files /dev/null and b/anylink/web/public/批量添加用户模版.xlsx differ
diff --git a/anylink/web/src/components/audit/Access.vue b/anylink/web/src/components/audit/Access.vue
new file mode 100644
index 0000000..bdab61b
--- /dev/null
+++ b/anylink/web/src/components/audit/Access.vue
@@ -0,0 +1,321 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置搜索
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/anylink/web/src/components/audit/ActLog.vue b/anylink/web/src/components/audit/ActLog.vue
new file mode 100644
index 0000000..ab0e79e
--- /dev/null
+++ b/anylink/web/src/components/audit/ActLog.vue
@@ -0,0 +1,263 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置搜索
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.value}}
+
+
+
+
+
+
+
+
+
+
+ {{ row.os == item? value: "" }}
+
+ 型号:
+ {{ row.device_type }} / {{ row.platform_version }}
+ -
+
+
+
+
+
+
+ {{ row.client == item? value: "" }}
+
+ {{ row.version }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/anylink/web/src/layout/LayoutAside.vue b/anylink/web/src/layout/LayoutAside.vue
index 332190a..cfbbc59 100644
--- a/anylink/web/src/layout/LayoutAside.vue
+++ b/anylink/web/src/layout/LayoutAside.vue
@@ -56,7 +56,7 @@
用户组列表
-
+
调试信息
@@ -91,4 +91,9 @@ export default {
.layout-menu {
height: 100%;
}
+
+.el-menu-item a {
+ display: block;
+ color: #fff;
+}
diff --git a/anylink/web/src/pages/Home.vue b/anylink/web/src/pages/Home.vue
index 9c071fe..02d0781 100644
--- a/anylink/web/src/pages/Home.vue
+++ b/anylink/web/src/pages/Home.vue
@@ -230,6 +230,9 @@ export default {
case "mem": this.formatMem(data); break;
}
}).catch(error => {
+ if (error.response.status === 401) {
+ return ;
+ }
this.$message.error('哦,请求出错');
console.log(error);
});
diff --git a/anylink/web/src/pages/group/List.vue b/anylink/web/src/pages/group/List.vue
index 72ab1b9..3a7ed31 100644
--- a/anylink/web/src/pages/group/List.vue
+++ b/anylink/web/src/pages/group/List.vue
@@ -191,9 +191,10 @@
BYTE/S
-
+
+ v-model="ruleForm.allow_lan"
+ active-text="开启后 用户本地所在网段将不通过anylink加密传输">
@@ -235,23 +236,23 @@
LDAP
-
+
-
+
-
+
-
+
@@ -259,14 +260,17 @@
-
+
+
+
+
-
+
-
+
@@ -359,13 +363,36 @@
- 保存
- 取消
+
+ 测试登录
+
+ 保存
+ 取消
-
+
+
+
+
+
+
+
+
+
+
+ 登录
+ 取 消
+
+
+
@@ -399,6 +426,7 @@ export default {
addr:"",
tls:false,
base_dn:"",
+ object_class:"person",
search_attr:"sAMAccountName",
member_of:"",
bind_name:"",
@@ -415,6 +443,21 @@ export default {
link_acl: [],
auth : {},
},
+ authLoginDialog : false,
+ authLoginLoading : false,
+ authLoginForm : {
+ name : "",
+ pwd : "",
+ },
+ authLoginRules: {
+ name: [
+ {required: true, message: '请输入账号', trigger: 'blur'},
+ ],
+ pwd: [
+ {required: true, message: '请输入密码', trigger: 'blur'},
+ {min: 6, message: '长度至少 6 个字符', trigger: 'blur'}
+ ],
+ },
rules: {
name: [
{required: true, message: '请输入组名', trigger: 'blur'},
@@ -437,7 +480,7 @@ export default {
{required: true, message: '请输入服务器地址(含端口)', trigger: 'blur'}
],
"auth.ldap.bind_name": [
- {required: true, message: '请输入管理员账号', trigger: 'blur'}
+ {required: true, message: '请输入管理员 DN', trigger: 'blur'}
],
"auth.ldap.bind_pwd": [
{required: true, message: '请输入管理员密码', trigger: 'blur'}
@@ -445,6 +488,9 @@ export default {
"auth.ldap.base_dn": [
{required: true, message: '请输入Base DN值', trigger: 'blur'}
],
+ "auth.ldap.object_class": [
+ {required: true, message: '请输入用户对象类', trigger: 'blur'}
+ ],
"auth.ldap.search_attr": [
{required: true, message: '请输入用户唯一ID', trigger: 'blur'}
],
@@ -457,6 +503,9 @@ export default {
this.ruleForm.auth = JSON.parse(JSON.stringify(this.defAuth));
return ;
}
+ if (row.auth.type == "ldap" && ! row.auth.ldap.object_class) {
+ row.auth.ldap.object_class = this.defAuth.ldap.object_class;
+ }
this.ruleForm.auth = Object.assign(JSON.parse(JSON.stringify(this.defAuth)), row.auth);
},
handleDel(row) {
@@ -549,6 +598,44 @@ export default {
});
});
},
+ testAuthLogin() {
+ this.$refs["authLoginForm"].validate((valid) => {
+ if (!valid) {
+ console.log('error submit!!');
+ return false;
+ }
+ this.authLoginLoading = true;
+ axios.post('/group/auth_login', {name:this.authLoginForm.name,
+ pwd:this.authLoginForm.pwd,
+ auth:this.ruleForm.auth}).then(resp => {
+ const rdata = resp.data;
+ if (rdata.code === 0) {
+ this.$message.success("登录成功");
+ } else {
+ this.$message.error(rdata.msg);
+ }
+ this.authLoginLoading = false;
+ console.log(rdata);
+ }).catch(error => {
+ this.$message.error('哦,请求出错');
+ console.log(error);
+ this.authLoginLoading = false;
+ });
+ });
+ },
+ openAuthLoginDialog() {
+ this.$refs["ruleForm"].validate((valid) => {
+ if (!valid) {
+ console.log('error submit!!');
+ return false;
+ }
+ this.authLoginDialog = true;
+ // set authLoginFormName focus
+ this.$nextTick(() => {
+ this.$refs['authLoginFormName'].focus();
+ });
+ });
+ },
resetForm(formName) {
this.$refs[formName].resetFields();
},
@@ -598,4 +685,20 @@ export default {
.el-select {
width: 80px;
}
+
+::v-deep .valgin-dialog{
+ display: flex;
+ flex-direction: column;
+ margin:0 !important;
+ position:absolute;
+ top:50%;
+ left:50%;
+ transform:translate(-50%,-50%);
+ max-height:calc(100% - 30px);
+ max-width:calc(100% - 30px);
+}
+::v-deep .valgin-dialog .el-dialog__body{
+ flex:1;
+ overflow: auto;
+}
diff --git a/anylink/web/src/pages/set/Audit.vue b/anylink/web/src/pages/set/Audit.vue
index 4e3a19e..e85de33 100644
--- a/anylink/web/src/pages/set/Audit.vue
+++ b/anylink/web/src/pages/set/Audit.vue
@@ -1,300 +1,61 @@