Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	server/handler/pool_test.go
This commit is contained in:
bjdgyc 2021-08-01 21:00:07 +08:00
commit 2a66df55b0
24 changed files with 14357 additions and 285 deletions

View File

@ -18,7 +18,7 @@ COPY --from=builder_node /web/ui /anylink/server/ui
#TODO 本地打包时使用镜像 #TODO 本地打包时使用镜像
#RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories #RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache git RUN apk add --no-cache git gcc
RUN cd /anylink/server;go build -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" \ RUN cd /anylink/server;go build -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" \
&& /anylink/server/anylink tool -v && /anylink/server/anylink tool -v

View File

@ -77,7 +77,7 @@ sudo ./anylink
## Config ## Config
> 默认配置文件内有详细的注释,根据注释填写配置即可。 > 示例配置文件内有详细的注释,根据注释填写配置即可。
```shell ```shell
# 生成后台密码 # 生成后台密码
@ -89,11 +89,11 @@ sudo ./anylink
> 数据库配置示例 > 数据库配置示例
| db_type | db_source | | db_type | db_source |
| ---- | ---- | | -------- | ------------------------------------------------------ |
| sqlite3 | ./conf/anylink.db | | sqlite3 | ./conf/anylink.db |
| mysql | user:password@tcp(127.0.0.1:3306)/anylink?charset=utf8 | | mysql | user:password@tcp(127.0.0.1:3306)/anylink?charset=utf8 |
| postgres | user:password@localhost/anylink?sslmode=verify-full | | postgres | user:password@localhost/anylink?sslmode=verify-full |
> 示例配置文件 > 示例配置文件
> >
@ -181,41 +181,48 @@ systemd 脚本放入:
docker pull bjdgyc/anylink:latest docker pull bjdgyc/anylink:latest
``` ```
2. 生成密码 2. 查看命令信息
```bash
docker run -it --rm bjdgyc/anylink -h
```
3. 生成密码
```bash ```bash
docker run -it --rm bjdgyc/anylink tool -p 123456 docker run -it --rm bjdgyc/anylink tool -p 123456
#Passwd:$2a$10$lCWTCcGmQdE/4Kb1wabbLelu4vY/cUwBwN64xIzvXcihFgRzUvH2a #Passwd:$2a$10$lCWTCcGmQdE/4Kb1wabbLelu4vY/cUwBwN64xIzvXcihFgRzUvH2a
``` ```
3. 生成jwt secret 4. 生成jwt secret
```bash ```bash
docker run -it --rm bjdgyc/anylink tool -s docker run -it --rm bjdgyc/anylink tool -s
#Secret:9qXoIhY01jqhWIeIluGliOS4O_rhcXGGGu422uRZ1JjZxIZmh17WwzW36woEbA #Secret:9qXoIhY01jqhWIeIluGliOS4O_rhcXGGGu422uRZ1JjZxIZmh17WwzW36woEbA
``` ```
4. 启动容器 5. 启动容器
```bash ```bash
docker run -itd --name anylink --privileged \ docker run -itd --name anylink --privileged \
-p 443:443 -p 8800:8800 \ -p 443:443 -p 8800:8800 \
--restart=always \ --restart=always \
bjdgyc/anylink bjdgyc/anylink
``` ```
5. 使用自定义参数启动容器 6. 使用自定义参数启动容器
```bash ```bash
# 参数可以参考 -h 命令
docker run -itd --name anylink --privileged \ docker run -itd --name anylink --privileged \
-e IPV4_CIDR=192.168.10.0/24 \ -e IPV4_CIDR=192.168.10.0/24 \
-p 443:443 -p 8800:8800 \ -p 443:443 -p 8800:8800 \
--restart=always \ --restart=always \
bjdgyc/anylink \ bjdgyc/anylink \
-c=/etc/server.toml --admin_addr=:8080 -c=/etc/server.toml --ip_lease = 1209600 \ # IP地址租约时长
``` ```
6. 构建镜像 7. 构建镜像
```bash ```bash
#获取仓库源码 #获取仓库源码
@ -247,6 +254,7 @@ QQ群共享文件有相关软件下载
<details> <details>
<summary>展开查看</summary> <summary>展开查看</summary>
![system.jpg](screenshot/system.jpg) ![system.jpg](screenshot/system.jpg)
![setting.jpg](screenshot/setting.jpg) ![setting.jpg](screenshot/setting.jpg)
![users.jpg](screenshot/users.jpg) ![users.jpg](screenshot/users.jpg)
@ -264,7 +272,3 @@ QQ群共享文件有相关软件下载
<a href="https://www.jetbrains.com"> <a href="https://www.jetbrains.com">
<img src="screenshot/jetbrains.png" width="200" height="200" alt="jetbrains.png" /> <img src="screenshot/jetbrains.png" width="200" height="200" alt="jetbrains.png" />
</a> </a>

View File

@ -25,6 +25,8 @@ echo "编译二进制文件"
cd $cpath/server cd $cpath/server
rm -rf ui rm -rf ui
cp -rf $cpath/web/ui . cp -rf $cpath/web/ui .
#国内可替换源加快速度
export GOPROXY=https://goproxy.io
go build -v -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" go build -v -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)"
RETVAL $? RETVAL $?

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
"text/template" "text/template"
@ -141,7 +142,7 @@ func UserOtpQr(w http.ResponseWriter, r *http.Request) {
return return
} }
issuer := base.Cfg.Issuer issuer := url.QueryEscape(base.Cfg.Issuer)
qrstr := fmt.Sprintf("otpauth://totp/%s:%s?issuer=%s&secret=%s", issuer, user.Email, issuer, user.OtpSecret) qrstr := fmt.Sprintf("otpauth://totp/%s:%s?issuer=%s&secret=%s", issuer, user.Email, issuer, user.OtpSecret)
qr, _ := qrcode.New(qrstr, qrcode.High) qr, _ := qrcode.New(qrstr, qrcode.High)

View File

@ -58,7 +58,7 @@ func StartAdmin() {
r.HandleFunc("/debug/pprof/profile", pprof.Profile).Name("debug") r.HandleFunc("/debug/pprof/profile", pprof.Profile).Name("debug")
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol).Name("debug") r.HandleFunc("/debug/pprof/symbol", pprof.Symbol).Name("debug")
r.HandleFunc("/debug/pprof/trace", pprof.Trace).Name("debug") r.HandleFunc("/debug/pprof/trace", pprof.Trace).Name("debug")
r.HandleFunc("/debug/pprof", location("/debug/pprof/")) r.HandleFunc("/debug/pprof", location("/debug/pprof/")).Name("debug")
r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index).Name("debug") r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index).Name("debug")
} }

View File

@ -5,8 +5,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"github.com/spf13/viper"
) )
const ( const (
@ -31,6 +29,7 @@ var (
type ServerConfig struct { type ServerConfig struct {
// LinkAddr string `json:"link_addr"` // LinkAddr string `json:"link_addr"`
Conf string `json:"conf"`
ServerAddr string `json:"server_addr"` ServerAddr string `json:"server_addr"`
ServerDTLSAddr string `json:"server_dtls_addr"` ServerDTLSAddr string `json:"server_dtls_addr"`
ServerDTLS bool `json:"server_dtls"` ServerDTLS bool `json:"server_dtls"`
@ -65,7 +64,7 @@ type ServerConfig struct {
MobileDpd int `json:"mobile_dpd"` MobileDpd int `json:"mobile_dpd"`
SessionTimeout int `json:"session_timeout"` // in seconds SessionTimeout int `json:"session_timeout"` // in seconds
AuthTimeout int `json:"auth_timeout"` // in seconds // AuthTimeout int `json:"auth_timeout"` // in seconds
} }
func initServerCfg() { func initServerCfg() {
@ -82,6 +81,10 @@ func initServerCfg() {
// Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath) // Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
// Cfg.LogPath = getAbsPath(base, Cfg.LogPath) // Cfg.LogPath = getAbsPath(base, Cfg.LogPath)
if Cfg.AdminPass == defaultPwd {
fmt.Fprintln(os.Stderr, "=== 使用默认的admin_pass有安全风险请设置新的admin_pass ===")
}
if Cfg.JwtSecret == defaultJwt { if Cfg.JwtSecret == defaultJwt {
fmt.Fprintln(os.Stderr, "=== 使用默认的jwt_secret有安全风险请设置新的jwt_secret ===") fmt.Fprintln(os.Stderr, "=== 使用默认的jwt_secret有安全风险请设置新的jwt_secret ===")
} }
@ -115,13 +118,13 @@ func initCfg() {
for _, v := range configs { for _, v := range configs {
if v.Name == tag { if v.Name == tag {
if v.Typ == cfgStr { if v.Typ == cfgStr {
value.SetString(viper.GetString(v.Name)) value.SetString(linkViper.GetString(v.Name))
} }
if v.Typ == cfgInt { if v.Typ == cfgInt {
value.SetInt(int64(viper.GetInt(v.Name))) value.SetInt(int64(linkViper.GetInt(v.Name)))
} }
if v.Typ == cfgBool { if v.Typ == cfgBool {
value.SetBool(viper.GetBool(v.Name)) value.SetBool(linkViper.GetBool(v.Name))
} }
} }
} }
@ -150,9 +153,6 @@ func ServerCfg2Slice() []SCfg {
value := s.Field(i) value := s.Field(i)
tag := field.Tag.Get("json") tag := field.Tag.Get("json")
usage, env := getUsageEnv(tag) usage, env := getUsageEnv(tag)
if usage == "" {
continue
}
datas = append(datas, SCfg{Name: tag, Env: env, Info: usage, Data: value.Interface()}) datas = append(datas, SCfg{Name: tag, Env: env, Info: usage, Data: value.Interface()})
} }

View File

@ -1,8 +1,10 @@
package base package base
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"reflect"
"runtime" "runtime"
"strings" "strings"
@ -14,25 +16,26 @@ import (
var ( var (
// 提交id // 提交id
CommitId string CommitId string
// 配置文件
cfgFile string
// pass明文 // pass明文
passwd string passwd string
// 生成密钥 // 生成密钥
secret bool secret bool
// 显示版本信息 // 显示版本信息
rev bool rev bool
// 获取env名称 // 输出debug信息
env bool debug bool
// Used for flags. // Used for flags.
runSrv bool runSrv bool
rootCmd *cobra.Command linkViper *viper.Viper
rootCmd *cobra.Command
) )
// Execute executes the root command. // Execute executes the root command.
func execute() { func execute() {
initCmd()
err := rootCmd.Execute() err := rootCmd.Execute()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -40,13 +43,25 @@ func execute() {
} }
// viper.Debug() // viper.Debug()
ref := reflect.ValueOf(linkViper)
s := ref.Elem()
ee := s.FieldByName("env")
if ee.Kind() != reflect.Map {
panic("Viper env is err")
}
rr := ee.MapRange()
for rr.Next() {
// fmt.Println(rr.Key(), rr.Value())
envs[rr.Key().String()] = rr.Value().String()
}
if !runSrv { if !runSrv {
os.Exit(0) os.Exit(0)
} }
} }
func init() { func initCmd() {
linkViper = viper.New()
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
Use: "anylink", Use: "anylink",
Short: "AnyLink VPN Server", Short: "AnyLink VPN Server",
@ -57,43 +72,44 @@ func init() {
}, },
} }
cobra.OnInitialize(func() { linkViper.SetEnvPrefix("link")
viper.SetConfigFile(cfgFile)
viper.AutomaticEnv()
if cfgFile == "" {
// 没有配置文件,不做处理
return
}
err := viper.ReadInConfig()
if err != nil {
fmt.Println("Using config file:", err)
}
})
viper.SetEnvPrefix("link")
// 基础配置 // 基础配置
rootCmd.Flags().StringVarP(&cfgFile, "conf", "c", "", "config file")
for _, v := range configs { for _, v := range configs {
if v.Typ == cfgStr { if v.Typ == cfgStr {
rootCmd.Flags().String(v.Name, v.ValStr, v.Usage) rootCmd.Flags().StringP(v.Name, v.Short, v.ValStr, v.Usage)
} }
if v.Typ == cfgInt { if v.Typ == cfgInt {
rootCmd.Flags().Int(v.Name, v.ValInt, v.Usage) rootCmd.Flags().IntP(v.Name, v.Short, v.ValInt, v.Usage)
} }
if v.Typ == cfgBool { if v.Typ == cfgBool {
rootCmd.Flags().Bool(v.Name, v.ValBool, v.Usage) rootCmd.Flags().BoolP(v.Name, v.Short, v.ValBool, v.Usage)
} }
_ = viper.BindPFlag(v.Name, rootCmd.Flags().Lookup(v.Name)) _ = linkViper.BindPFlag(v.Name, rootCmd.Flags().Lookup(v.Name))
_ = viper.BindEnv(v.Name) _ = linkViper.BindEnv(v.Name)
// viper.SetDefault(v.Name, v.Value) // viper.SetDefault(v.Name, v.Value)
} }
rootCmd.AddCommand(initToolCmd()) rootCmd.AddCommand(initToolCmd())
cobra.OnInitialize(func() {
linkViper.AutomaticEnv()
conf := linkViper.GetString("conf")
_, err := os.Stat(conf)
if errors.Is(err, os.ErrNotExist) {
// 没有配置文件,不做处理
return
}
linkViper.SetConfigFile(conf)
err = linkViper.ReadInConfig()
if err != nil {
fmt.Println("Using config file:", err)
}
})
} }
func initToolCmd() *cobra.Command { func initToolCmd() *cobra.Command {
@ -106,7 +122,7 @@ func initToolCmd() *cobra.Command {
toolCmd.Flags().BoolVarP(&rev, "version", "v", false, "display version info") toolCmd.Flags().BoolVarP(&rev, "version", "v", false, "display version info")
toolCmd.Flags().BoolVarP(&secret, "secret", "s", false, "generate a random jwt secret") toolCmd.Flags().BoolVarP(&secret, "secret", "s", false, "generate a random jwt secret")
toolCmd.Flags().StringVarP(&passwd, "passwd", "p", "", "convert the password plaintext") toolCmd.Flags().StringVarP(&passwd, "passwd", "p", "", "convert the password plaintext")
toolCmd.Flags().BoolVarP(&env, "env", "e", false, "list the config name and env key") toolCmd.Flags().BoolVarP(&debug, "debug", "d", false, "list the config viper.Debug() info")
toolCmd.Run = func(cmd *cobra.Command, args []string) { toolCmd.Run = func(cmd *cobra.Command, args []string) {
switch { switch {
@ -120,10 +136,8 @@ func initToolCmd() *cobra.Command {
case passwd != "": case passwd != "":
pass, _ := utils.PasswordHash(passwd) pass, _ := utils.PasswordHash(passwd)
fmt.Printf("Passwd:%s\n", pass) fmt.Printf("Passwd:%s\n", pass)
case env: case debug:
for k, v := range envs { linkViper.Debug()
fmt.Printf("%s => %s\n", k, v)
}
default: default:
fmt.Println("Using [anylink tool -h] for help") fmt.Println("Using [anylink tool -h] for help")
} }

View File

@ -6,11 +6,13 @@ const (
cfgBool cfgBool
defaultJwt = "abcdef.0123456789.abcdef" defaultJwt = "abcdef.0123456789.abcdef"
defaultPwd = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
) )
type config struct { type config struct {
Typ int Typ int
Name string Name string
Short string
Usage string Usage string
ValStr string ValStr string
ValInt int ValInt int
@ -18,6 +20,7 @@ type config struct {
} }
var configs = []config{ var configs = []config{
{Typ: cfgStr, Name: "conf", Usage: "config file", ValStr: "./conf/server.toml", Short: "c"},
{Typ: cfgStr, Name: "server_addr", Usage: "服务监听地址", ValStr: ":443"}, {Typ: cfgStr, Name: "server_addr", Usage: "服务监听地址", ValStr: ":443"},
{Typ: cfgBool, Name: "server_dtls", Usage: "开启DTLS", ValBool: false}, {Typ: cfgBool, Name: "server_dtls", Usage: "开启DTLS", ValBool: false},
{Typ: cfgStr, Name: "server_dtls_addr", Usage: "DTLS监听地址", ValStr: ":4433"}, {Typ: cfgStr, Name: "server_dtls_addr", Usage: "DTLS监听地址", ValStr: ":4433"},
@ -33,7 +36,7 @@ var configs = []config{
{Typ: cfgBool, Name: "pprof", Usage: "开启pprof", ValBool: false}, {Typ: cfgBool, Name: "pprof", Usage: "开启pprof", ValBool: false},
{Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"}, {Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"},
{Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"}, {Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"},
{Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"}, {Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: defaultPwd},
{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: defaultJwt}, {Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: defaultJwt},
{Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"}, {Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"},
{Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"}, {Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"},

View File

@ -1,4 +1,4 @@
#服务配置信息 #示例配置信息
#其他配置文件,可以使用绝对路径 #其他配置文件,可以使用绝对路径
#或者相对于 anylink 二进制文件的路径 #或者相对于 anylink 二进制文件的路径

View File

@ -1,8 +1,6 @@
package dbdata package dbdata
import ( import (
"encoding/json"
"github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/base"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@ -41,17 +39,24 @@ func initData() {
) )
// 判断是否初次使用 // 判断是否初次使用
s := &Setting{} install := &SettingInstall{}
err = One("name", InstallName, s) err = SettingGet(install)
if err == nil && s.Data == InstallData {
if err == nil && install.Installed {
// 已经安装过 // 已经安装过
return return
} }
// 发生错误
if err != ErrNotFound {
base.Fatal(err)
}
err = addInitData() err = addInitData()
if err != nil { if err != nil {
base.Fatal(err) base.Fatal(err)
} }
} }
func addInitData() error { func addInitData() error {
@ -74,9 +79,7 @@ func addInitData() error {
From: "vpn@xx.com", From: "vpn@xx.com",
Encryption: "None", Encryption: "None",
} }
v, _ := json.Marshal(smtp) err = SettingSessAdd(sess, smtp)
s := &Setting{Name: StructName(smtp), Data: string(v)}
_, err = sess.InsertOne(s)
if err != nil { if err != nil {
return err return err
} }
@ -87,16 +90,14 @@ func addInitData() error {
Banner: "您已接入公司网络,请按照公司规定使用。\n请勿进行非工作下载及视频行为", Banner: "您已接入公司网络,请按照公司规定使用。\n请勿进行非工作下载及视频行为",
AccountMail: accountMail, AccountMail: accountMail,
} }
v, _ = json.Marshal(other) err = SettingSessAdd(sess, other)
s = &Setting{Name: StructName(other), Data: string(v)}
_, err = sess.InsertOne(s)
if err != nil { if err != nil {
return err return err
} }
// Install // Install
install := &Setting{Name: InstallName, Data: InstallData} install := &SettingInstall{Installed: true}
_, err = sess.InsertOne(install) err = SettingSessAdd(sess, install)
if err != nil { if err != nil {
return err return err
} }

View File

@ -29,20 +29,20 @@ type ValData struct {
Note string `json:"note"` Note string `json:"note"`
} }
type Group struct { // type Group struct {
Id int `json:"id" xorm:"pk autoincr not null"` // Id int `json:"id" xorm:"pk autoincr not null"`
Name string `json:"name" xorm:"not null unique"` // Name string `json:"name" xorm:"not null unique"`
Note string `json:"note"` // Note string `json:"note"`
AllowLan bool `json:"allow_lan"` // AllowLan bool `json:"allow_lan"`
ClientDns []ValData `json:"client_dns"` // ClientDns []ValData `json:"client_dns"`
RouteInclude []ValData `json:"route_include"` // RouteInclude []ValData `json:"route_include"`
RouteExclude []ValData `json:"route_exclude"` // RouteExclude []ValData `json:"route_exclude"`
LinkAcl []GroupLinkAcl `json:"link_acl"` // LinkAcl []GroupLinkAcl `json:"link_acl"`
Bandwidth int `json:"bandwidth"` // 带宽限制 // Bandwidth int `json:"bandwidth"` // 带宽限制
Status int8 `json:"status"` // 1正常 // Status int8 `json:"status"` // 1正常
CreatedAt time.Time `json:"created_at"` // CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` // UpdatedAt time.Time `json:"updated_at"`
} // }
func GetGroupNames() []string { func GetGroupNames() []string {
var datas []Group var datas []Group

View File

@ -5,17 +5,17 @@ import (
"time" "time"
) )
type IpMap struct { // type IpMap struct {
Id int `json:"id" xorm:"pk autoincr not null"` // Id int `json:"id" xorm:"pk autoincr not null"`
IpAddr string `json:"ip_addr" xorm:"not null unique"` // IpAddr string `json:"ip_addr" xorm:"not null unique"`
MacAddr string `json:"mac_addr" xorm:"not null unique"` // MacAddr string `json:"mac_addr" xorm:"not null unique"`
Username string `json:"username"` // Username string `json:"username"`
Keep bool `json:"keep"` // 保留 ip-mac 绑定 // Keep bool `json:"keep"` // 保留 ip-mac 绑定
KeepTime time.Time `json:"keep_time"` // KeepTime time.Time `json:"keep_time"`
Note string `json:"note"` // 备注 // Note string `json:"note"` // 备注
LastLogin time.Time `json:"last_login"` // LastLogin time.Time `json:"last_login"`
UpdatedAt time.Time `json:"updated_at"` // UpdatedAt time.Time `json:"updated_at"`
} // }
func SetIpMap(v *IpMap) error { func SetIpMap(v *IpMap) error {
var err error var err error

View File

@ -3,17 +3,11 @@ package dbdata
import ( import (
"encoding/json" "encoding/json"
"reflect" "reflect"
"xorm.io/xorm"
) )
const ( type SettingInstall struct {
InstallName = "Install" Installed bool `json:"installed"`
InstallData = "OK"
)
type Setting struct {
Id int `json:"id" xorm:"pk autoincr not null"`
Name string `json:"name" xorm:"not null unique"`
Data string `json:"data" xorm:"Text"`
} }
type SettingSmtp struct { type SettingSmtp struct {
@ -42,29 +36,30 @@ func StructName(data interface{}) string {
return name return name
} }
func SettingAdd(data interface{}) error { func SettingSessAdd(sess *xorm.Session, data interface{}) error {
name := StructName(data) name := StructName(data)
v, _ := json.Marshal(data) v, _ := json.Marshal(data)
s := Setting{Name: name, Data: string(v)} s := &Setting{Name: name, Data: v}
err := Add(&s) _, err := sess.InsertOne(s)
return err return err
} }
func SettingSet(data interface{}) error { func SettingSet(data interface{}) error {
name := StructName(data) name := StructName(data)
v, _ := json.Marshal(data) v, _ := json.Marshal(data)
s := Setting{Data: string(v)} s := &Setting{Data: v}
err := Update("name", name, &s) err := Update("name", name, s)
return err return err
} }
func SettingGet(data interface{}) error { func SettingGet(data interface{}) error {
name := StructName(data) name := StructName(data)
s := Setting{Name: name} s := &Setting{Name: name}
err := One("name", name, &s) err := One("name", name, s)
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal([]byte(s.Data), data) err = json.Unmarshal(s.Data, data)
return err return err
} }

56
server/dbdata/tables.go Normal file
View File

@ -0,0 +1,56 @@
package dbdata
import (
"encoding/json"
"time"
)
type Group struct {
Id int `json:"id" xorm:"pk autoincr not null"`
Name string `json:"name" xorm:"varchar(60) not null unique"`
Note string `json:"note" xorm:"varchar(255)"`
AllowLan bool `json:"allow_lan" xorm:"Bool"`
ClientDns []ValData `json:"client_dns" xorm:"Text"`
RouteInclude []ValData `json:"route_include" xorm:"Text"`
RouteExclude []ValData `json:"route_exclude" xorm:"Text"`
LinkAcl []GroupLinkAcl `json:"link_acl" xorm:"Text"`
Bandwidth int `json:"bandwidth" xorm:"Int"` // 带宽限制
Status int8 `json:"status" xorm:"Int"` // 1正常
CreatedAt time.Time `json:"created_at" xorm:"DateTime created"`
UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
}
type User struct {
Id int `json:"id" xorm:"pk autoincr not null"`
Username string `json:"username" xorm:"varchar(60) not null unique"`
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"`
}
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 updated"`
UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
}
type Setting struct {
Id int `json:"id" xorm:"pk autoincr not null"`
Name string `json:"name" xorm:"varchar(60) not null unique"`
Data json.RawMessage `json:"data" xorm:"Text"`
UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
}

View File

@ -10,21 +10,21 @@ import (
"github.com/xlzd/gotp" "github.com/xlzd/gotp"
) )
type User struct { // type User struct {
Id int `json:"id" xorm:"pk autoincr not null"` // Id int `json:"id" xorm:"pk autoincr not null"`
Username string `json:"username" storm:"not null unique"` // Username string `json:"username" storm:"not null unique"`
Nickname string `json:"nickname"` // Nickname string `json:"nickname"`
Email string `json:"email"` // Email string `json:"email"`
// Password string `json:"password"` // // Password string `json:"password"`
PinCode string `json:"pin_code"` // PinCode string `json:"pin_code"`
OtpSecret string `json:"otp_secret"` // OtpSecret string `json:"otp_secret"`
DisableOtp bool `json:"disable_otp"` // 禁用otp // DisableOtp bool `json:"disable_otp"` // 禁用otp
Groups []string `json:"groups"` // Groups []string `json:"groups"`
Status int8 `json:"status"` // 1正常 // Status int8 `json:"status"` // 1正常
SendEmail bool `json:"send_email"` // SendEmail bool `json:"send_email"`
CreatedAt time.Time `json:"created_at"` // CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` // UpdatedAt time.Time `json:"updated_at"`
} // }
func SetUser(v *User) error { func SetUser(v *User) error {
var err error var err error

View File

@ -34,9 +34,8 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
return return
} }
// hdata := make([]byte, BufferSize) // hdata := make([]byte, BufferSize)
hb := getByteFull() pl := getPayload()
hdata := *hb n, err = conn.Read(pl.Data)
n, err = conn.Read(hdata)
if err != nil { if err != nil {
base.Error("read hdata: ", err) base.Error("read hdata: ", err)
return return
@ -48,7 +47,7 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
base.Error(err) base.Error(err)
} }
switch hdata[6] { switch pl.Data[6] {
case 0x07: // KEEPALIVE case 0x07: // KEEPALIVE
// do nothing // do nothing
// base.Debug("recv keepalive", cSess.IpAddr) // base.Debug("recv keepalive", cSess.IpAddr)
@ -57,19 +56,24 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
return return
case 0x03: // DPD-REQ case 0x03: // DPD-REQ
// base.Debug("recv DPD-REQ", cSess.IpAddr) // base.Debug("recv DPD-REQ", cSess.IpAddr)
if payloadOutCstp(cSess, sessdata.LTypeIPData, 0x04, nil) { pl.PType = 0x04
if payloadOutCstp(cSess, pl) {
return return
} }
case 0x04: case 0x04:
// log.Println("recv DPD-RESP") // log.Println("recv DPD-RESP")
case 0x00: // DATA case 0x00: // DATA
dataLen = binary.BigEndian.Uint16(hdata[4:6]) // 4,5 // 获取数据长度
if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[8:8+dataLen]) { dataLen = binary.BigEndian.Uint16(pl.Data[4:6]) // 4,5
// 去除数据头
copy(pl.Data, pl.Data[8:8+dataLen])
// 更新切片长度
pl.Data = pl.Data[:dataLen]
// pl.Data = append(pl.Data[:0], pl.Data[8:8+dataLen]...)
if payloadIn(cSess, pl) {
return return
} }
} }
putByte(hb)
} }
} }
@ -83,37 +87,44 @@ func cstpWrite(conn net.Conn, cSess *sessdata.ConnSession) {
var ( var (
err error err error
n int n int
// header []byte pl *sessdata.Payload
payload *sessdata.Payload
) )
for { for {
select { select {
case payload = <-cSess.PayloadOutCstp: case pl = <-cSess.PayloadOutCstp:
case <-cSess.CloseChan: case <-cSess.CloseChan:
return return
} }
if payload.LType != sessdata.LTypeIPData { if pl.LType != sessdata.LTypeIPData {
continue continue
} }
h := []byte{'S', 'T', 'F', 0x01, 0x00, 0x00, payload.PType, 0x00} if pl.PType == 0x00 {
hb := getByteZero() // 获取数据长度
header := *hb l := len(pl.Data)
header = append(header, h...) // 先扩容 +8
if payload.PType == 0x00 { // data pl.Data = pl.Data[:l+8]
binary.BigEndian.PutUint16(header[4:6], uint16(len(payload.Data))) // 数据后移
header = append(header, payload.Data...) 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...)
// 设置头类型
pl.Data[6] = pl.PType
} }
n, err = conn.Write(header)
n, err = conn.Write(pl.Data)
if err != nil { if err != nil {
base.Error("write err", err) base.Error("write err", err)
return return
} }
putByte(hb) putPayload(pl)
putPayload(payload)
// 限流设置 // 限流设置
err = cSess.RateLimit(n, false) err = cSess.RateLimit(n, false)

View File

@ -24,21 +24,22 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
}() }()
var ( var (
err error
n int
dead = time.Duration(cSess.CstpDpd+5) * time.Second dead = time.Duration(cSess.CstpDpd+5) * time.Second
) )
go dtlsWrite(conn, dSess, cSess) go dtlsWrite(conn, dSess, cSess)
for { for {
err := conn.SetReadDeadline(time.Now().Add(dead)) err = conn.SetReadDeadline(time.Now().Add(dead))
if err != nil { if err != nil {
base.Error("SetDeadline: ", err) base.Error("SetDeadline: ", err)
return return
} }
hb := getByteFull() pl := getPayload()
hdata := *hb n, err = conn.Read(pl.Data)
n, err := conn.Read(hdata)
if err != nil { if err != nil {
base.Error("read hdata: ", err) base.Error("read hdata: ", err)
return return
@ -50,7 +51,7 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
base.Error(err) base.Error(err)
} }
switch hdata[0] { switch pl.Data[0] {
case 0x07: // KEEPALIVE case 0x07: // KEEPALIVE
// do nothing // do nothing
// base.Debug("recv keepalive", cSess.IpAddr) // base.Debug("recv keepalive", cSess.IpAddr)
@ -59,18 +60,23 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
return return
case 0x03: // DPD-REQ case 0x03: // DPD-REQ
// base.Debug("recv DPD-REQ", cSess.IpAddr) // base.Debug("recv DPD-REQ", cSess.IpAddr)
if payloadOutDtls(cSess, dSess, sessdata.LTypeIPData, 0x04, nil) { pl.PType = 0x04
if payloadOutDtls(cSess, dSess, pl) {
return return
} }
case 0x04: case 0x04:
// base.Debug("recv DPD-RESP", cSess.IpAddr) // base.Debug("recv DPD-RESP", cSess.IpAddr)
case 0x00: // DATA case 0x00: // DATA
if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[1:n]) { // 去除数据头
// copy(pl.Data, pl.Data[1:n])
// 更新切片长度
// pl.Data = pl.Data[:n-1]
pl.Data = append(pl.Data[:0], pl.Data[1:n]...)
if payloadIn(cSess, pl) {
return return
} }
} }
putByte(hb)
} }
} }
@ -82,37 +88,42 @@ func dtlsWrite(conn net.Conn, dSess *sessdata.DtlsSession, cSess *sessdata.ConnS
}() }()
var ( var (
// header []byte pl *sessdata.Payload
payload *sessdata.Payload
) )
for { for {
// dtls优先推送数据 // dtls优先推送数据
select { select {
case payload = <-cSess.PayloadOutDtls: case pl = <-cSess.PayloadOutDtls:
case <-dSess.CloseChan: case <-dSess.CloseChan:
return return
} }
if payload.LType != sessdata.LTypeIPData { if pl.LType != sessdata.LTypeIPData {
continue continue
} }
// header = []byte{payload.PType} // header = []byte{payload.PType}
hb := getByteZero() if pl.PType == 0x00 { // data
header := *hb // 获取数据长度
header = append(header, payload.PType) l := len(pl.Data)
if payload.PType == 0x00 { // data // 先扩容 +1
header = append(header, payload.Data...) 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(header) n, err := conn.Write(pl.Data)
if err != nil { if err != nil {
base.Error("write err", err) base.Error("write err", err)
return return
} }
putByte(hb) putPayload(pl)
putPayload(payload)
// 限流设置 // 限流设置
err = cSess.RateLimit(n, false) err = cSess.RateLimit(n, false)

View File

@ -88,14 +88,14 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
}() }()
var ( var (
err error err error
payload *sessdata.Payload pl *sessdata.Payload
frame ethernet.Frame frame ethernet.Frame
) )
for { for {
select { select {
case payload = <-cSess.PayloadIn: case pl = <-cSess.PayloadIn:
case <-cSess.CloseChan: case <-cSess.CloseChan:
return return
} }
@ -103,17 +103,15 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
// var frame ethernet.Frame // var frame ethernet.Frame
fb := getByteFull() fb := getByteFull()
frame = *fb frame = *fb
switch payload.LType { switch pl.LType {
default: default:
// log.Println(payload) // log.Println(payload)
case sessdata.LTypeEthernet: case sessdata.LTypeEthernet:
copy(frame, payload.Data) copy(frame, pl.Data)
frame = frame[:len(payload.Data)] frame = frame[:len(pl.Data)]
case sessdata.LTypeIPData: // 需要转换成 Ethernet 数据 case sessdata.LTypeIPData: // 需要转换成 Ethernet 数据
data := payload.Data ip_src := waterutil.IPv4Source(pl.Data)
if waterutil.IsIPv6(pl.Data) || !ip_src.Equal(cSess.IpAddr) {
ip_src := waterutil.IPv4Source(data)
if waterutil.IsIPv6(data) || !ip_src.Equal(cSess.IpAddr) {
// 过滤掉IPv6的数据 // 过滤掉IPv6的数据
// 非分配给客户端ip直接丢弃 // 非分配给客户端ip直接丢弃
continue continue
@ -122,7 +120,7 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) // packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("get:", packet) // fmt.Println("get:", packet)
ip_dst := waterutil.IPv4Destination(data) ip_dst := waterutil.IPv4Destination(pl.Data)
// fmt.Println("get:", ip_src, ip_dst) // fmt.Println("get:", ip_src, ip_dst)
var dstHw net.HardwareAddr var dstHw net.HardwareAddr
@ -142,8 +140,8 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
} }
// fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr) // fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr)
frame.Prepare(dstHw, cSess.MacHw, ethernet.NotTagged, ethernet.IPv4, len(data)) frame.Prepare(dstHw, cSess.MacHw, ethernet.NotTagged, ethernet.IPv4, len(pl.Data))
copy(frame[12+2:], data) copy(frame[12+2:], pl.Data)
} }
// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default) // packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
@ -155,7 +153,7 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
} }
putByte(fb) putByte(fb)
putPayload(payload) putPayload(pl)
} }
} }
@ -168,7 +166,7 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
var ( var (
err error err error
n int n int
buf []byte data []byte
frame ethernet.Frame frame ethernet.Frame
) )
@ -193,7 +191,7 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
continue continue
case ethernet.IPv4: case ethernet.IPv4:
// 发送IP数据 // 发送IP数据
data := frame.Payload() data = frame.Payload()
ip_dst := waterutil.IPv4Destination(data) ip_dst := waterutil.IPv4Destination(data)
if !ip_dst.Equal(cSess.IpAddr) { if !ip_dst.Equal(cSess.IpAddr) {
@ -205,7 +203,12 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) // packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("put:", packet) // fmt.Println("put:", packet)
if payloadOut(cSess, sessdata.LTypeIPData, 0x00, data) { pl := getPayload()
// 拷贝数据到pl
copy(pl.Data, data)
// 更新切片长度
pl.Data = pl.Data[:len(data)]
if payloadOut(cSess, pl) {
return return
} }
@ -226,7 +229,7 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
// 返回ARP数据 // 返回ARP数据
src := &arpdis.Addr{IP: cSess.IpAddr, HardwareAddr: cSess.MacHw} src := &arpdis.Addr{IP: cSess.IpAddr, HardwareAddr: cSess.MacHw}
dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()} dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()}
buf, err = arpdis.NewARPReply(src, dst) data, err = arpdis.NewARPReply(src, dst)
if err != nil { if err != nil {
base.Error(err) base.Error(err)
return return
@ -243,7 +246,15 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
copy(addr.HardwareAddr, frame.Source()) copy(addr.HardwareAddr, frame.Source())
arpdis.Add(addr) arpdis.Add(addr)
if payloadIn(cSess, sessdata.LTypeEthernet, 0x00, buf) { pl := getPayload()
// 设置为二层数据类型
pl.LType = sessdata.LTypeEthernet
// 拷贝数据到pl
copy(pl.Data, data)
// 更新切片长度
pl.Data = pl.Data[:len(data)]
if payloadIn(cSess, pl) {
return return
} }

View File

@ -69,24 +69,24 @@ func tunWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
}() }()
var ( var (
err error err error
payload *sessdata.Payload pl *sessdata.Payload
) )
for { for {
select { select {
case payload = <-cSess.PayloadIn: case pl = <-cSess.PayloadIn:
case <-cSess.CloseChan: case <-cSess.CloseChan:
return return
} }
_, err = ifce.Write(payload.Data) _, err = ifce.Write(pl.Data)
if err != nil { if err != nil {
base.Error("tun Write err", err) base.Error("tun Write err", err)
return return
} }
putPayload(payload) putPayload(pl)
} }
} }
@ -102,14 +102,16 @@ func tunRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
for { for {
// data := make([]byte, BufferSize) // data := make([]byte, BufferSize)
hb := getByteFull() pl := getPayload()
data := *hb n, err = ifce.Read(pl.Data)
n, err = ifce.Read(data)
if err != nil { if err != nil {
base.Error("tun Read err", n, err) base.Error("tun Read err", n, err)
return return
} }
// 更新数据长度
pl.Data = (pl.Data)[:n]
// data = data[:n] // data = data[:n]
// ip_src := waterutil.IPv4Source(data) // ip_src := waterutil.IPv4Source(data)
// ip_dst := waterutil.IPv4Destination(data) // ip_dst := waterutil.IPv4Destination(data)
@ -118,10 +120,8 @@ func tunRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) // packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("read:", packet) // fmt.Println("read:", packet)
if payloadOut(cSess, sessdata.LTypeIPData, 0x00, data[:n]) { if payloadOut(cSess, pl) {
return return
} }
putByte(hb)
} }
} }

View File

@ -6,18 +6,9 @@ import (
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
) )
func payloadIn(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool { func payloadIn(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
pl := getPayload()
pl.LType = lType
pl.PType = pType
pl.Data = append(pl.Data, data...)
return payloadInData(cSess, pl)
}
func payloadInData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool {
// 进行Acl规则判断 // 进行Acl规则判断
check := checkLinkAcl(cSess.Group, payload) check := checkLinkAcl(cSess.Group, pl)
if !check { if !check {
// 校验不通过直接丢弃 // 校验不通过直接丢弃
return false return false
@ -25,7 +16,7 @@ func payloadInData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool
closed := false closed := false
select { select {
case cSess.PayloadIn <- payload: case cSess.PayloadIn <- pl:
case <-cSess.CloseChan: case <-cSess.CloseChan:
closed = true closed = true
} }
@ -33,21 +24,16 @@ func payloadInData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool
return closed return closed
} }
func payloadOut(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool { func payloadOut(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
dSess := cSess.GetDtlsSession() dSess := cSess.GetDtlsSession()
if dSess == nil { if dSess == nil {
return payloadOutCstp(cSess, lType, pType, data) return payloadOutCstp(cSess, pl)
} else { } else {
return payloadOutDtls(cSess, dSess, lType, pType, data) return payloadOutDtls(cSess, dSess, pl)
} }
} }
func payloadOutCstp(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool { func payloadOutCstp(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
pl := getPayload()
pl.LType = lType
pl.PType = pType
pl.Data = append(pl.Data, data...)
closed := false closed := false
select { select {
@ -59,12 +45,7 @@ func payloadOutCstp(cSess *sessdata.ConnSession, lType sessdata.LType, pType byt
return closed return closed
} }
func payloadOutDtls(cSess *sessdata.ConnSession, dSess *sessdata.DtlsSession, lType sessdata.LType, pType byte, data []byte) bool { func payloadOutDtls(cSess *sessdata.ConnSession, dSess *sessdata.DtlsSession, pl *sessdata.Payload) bool {
pl := getPayload()
pl.LType = lType
pl.PType = pType
pl.Data = append(pl.Data, data...)
select { select {
case cSess.PayloadOutDtls <- pl: case cSess.PayloadOutDtls <- pl:
case <-dSess.CloseChan: case <-dSess.CloseChan:
@ -74,15 +55,16 @@ func payloadOutDtls(cSess *sessdata.ConnSession, dSess *sessdata.DtlsSession, lT
} }
// Acl规则校验 // Acl规则校验
func checkLinkAcl(group *dbdata.Group, payload *sessdata.Payload) bool { func checkLinkAcl(group *dbdata.Group, pl *sessdata.Payload) bool {
if payload.LType == sessdata.LTypeIPData && payload.PType == 0x00 && len(group.LinkAcl) > 0 { if pl.LType == sessdata.LTypeIPData && pl.PType == 0x00 && len(group.LinkAcl) > 0 {
} else { } else {
return true return true
} }
ip_dst := waterutil.IPv4Destination(payload.Data) data := pl.Data
ip_port := waterutil.IPv4DestinationPort(payload.Data) ip_dst := waterutil.IPv4Destination(data)
ip_proto := waterutil.IPv4Protocol(payload.Data) ip_port := waterutil.IPv4DestinationPort(data)
ip_proto := waterutil.IPv4Protocol(data)
// fmt.Println("sent:", ip_dst, ip_port) // fmt.Println("sent:", ip_dst, ip_port)
// 优先放行dns端口 // 优先放行dns端口

View File

@ -3,13 +3,21 @@ package handler
import ( import (
"sync" "sync"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/sessdata" "github.com/bjdgyc/anylink/sessdata"
) )
// 不允许直接修改
// [6] => PType
var plHeader = []byte{'S', 'T', 'F', 0x01, 0x00, 0x00, 0x00, 0x00}
var plPool = sync.Pool{ var plPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
b := make([]byte, BufferSize)
pl := sessdata.Payload{ pl := sessdata.Payload{
Data: make([]byte, 0, BufferSize), LType: sessdata.LTypeIPData,
PType: 0x00,
Data: b,
} }
// fmt.Println("plPool-init", len(pl.Data), cap(pl.Data)) // fmt.Println("plPool-init", len(pl.Data), cap(pl.Data))
return &pl return &pl
@ -22,15 +30,21 @@ func getPayload() *sessdata.Payload {
} }
func putPayload(pl *sessdata.Payload) { func putPayload(pl *sessdata.Payload) {
pl.LType = 0 // 错误数据丢弃
pl.PType = 0 if cap(pl.Data) != BufferSize {
pl.Data = pl.Data[:0] base.Warn("payload cap is err", cap(pl.Data))
return
}
pl.LType = sessdata.LTypeIPData
pl.PType = 0x00
pl.Data = pl.Data[:BufferSize]
plPool.Put(pl) plPool.Put(pl)
} }
var bytePool = sync.Pool{ var bytePool = sync.Pool{
New: func() interface{} { New: func() interface{} {
b := make([]byte, 0, BufferSize) b := make([]byte, BufferSize)
// fmt.Println("bytePool-init") // fmt.Println("bytePool-init")
return &b return &b
}, },
@ -38,15 +52,15 @@ var bytePool = sync.Pool{
func getByteZero() *[]byte { func getByteZero() *[]byte {
b := bytePool.Get().(*[]byte) b := bytePool.Get().(*[]byte)
*b = (*b)[:0]
return b return b
} }
func getByteFull() *[]byte { func getByteFull() *[]byte {
b := bytePool.Get().(*[]byte) b := bytePool.Get().(*[]byte)
*b = (*b)[:BufferSize]
return b return b
} }
func putByte(b *[]byte) { func putByte(b *[]byte) {
*b = (*b)[:0] *b = (*b)[:BufferSize]
bytePool.Put(b) bytePool.Put(b)
} }

View File

@ -8,8 +8,8 @@ const (
) )
type Payload struct { type Payload struct {
PType byte // payload types
LType LType // LinkType LType LType // LinkType
PType byte // payload types
Data []byte Data []byte
} }

13976
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -181,18 +181,20 @@
<el-col :span="4"> <el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle <el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.client_dns)"></el-button> @click.prevent="addDomain(ruleForm.client_dns)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.client_dns)"></el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-for="(item,index) in ruleForm.client_dns" <el-row v-for="(item,index) in ruleForm.client_dns"
:key="index" style="margin-bottom: 5px" gutter="10"> :key="index" style="margin-bottom: 5px" :gutter="10">
<el-col :span="10"> <el-col :span="10">
<el-input v-model="item.val"></el-input> <el-input v-model="item.val"></el-input>
</el-col> </el-col>
<el-col :span="14"> <el-col :span="12">
<el-input v-model="item.note" placeholder="备注"></el-input> <el-input v-model="item.note" placeholder="备注"></el-input>
</el-col> </el-col>
<el-col :span="2">
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.client_dns,index)"></el-button>
</el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -202,18 +204,20 @@
<el-col :span="4"> <el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle <el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.route_include)"></el-button> @click.prevent="addDomain(ruleForm.route_include)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_include)"></el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-for="(item,index) in ruleForm.route_include" <el-row v-for="(item,index) in ruleForm.route_include"
:key="index" style="margin-bottom: 5px" gutter="10"> :key="index" style="margin-bottom: 5px" :gutter="10">
<el-col :span="10"> <el-col :span="10">
<el-input v-model="item.val"></el-input> <el-input v-model="item.val"></el-input>
</el-col> </el-col>
<el-col :span="14"> <el-col :span="12">
<el-input v-model="item.note" placeholder="备注"></el-input> <el-input v-model="item.note" placeholder="备注"></el-input>
</el-col> </el-col>
<el-col :span="2">
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_include,index)"></el-button>
</el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -223,18 +227,20 @@
<el-col :span="4"> <el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle <el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.route_exclude)"></el-button> @click.prevent="addDomain(ruleForm.route_exclude)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_exclude)"></el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-for="(item,index) in ruleForm.route_exclude" <el-row v-for="(item,index) in ruleForm.route_exclude"
:key="index" style="margin-bottom: 5px" gutter="10"> :key="index" style="margin-bottom: 5px" :gutter="10">
<el-col :span="10"> <el-col :span="10">
<el-input v-model="item.val"></el-input> <el-input v-model="item.val"></el-input>
</el-col> </el-col>
<el-col :span="14"> <el-col :span="12">
<el-input v-model="item.note" placeholder="备注"></el-input> <el-input v-model="item.note" placeholder="备注"></el-input>
</el-col> </el-col>
<el-col :span="2">
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_exclude,index)"></el-button>
</el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -244,13 +250,11 @@
<el-col :span="4"> <el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle <el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.link_acl)"></el-button> @click.prevent="addDomain(ruleForm.link_acl)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.link_acl)"></el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-for="(item,index) in ruleForm.link_acl" <el-row v-for="(item,index) in ruleForm.link_acl"
:key="index" style="margin-bottom: 5px" gutter="5"> :key="index" style="margin-bottom: 5px" :gutter="5">
<el-col :span="11"> <el-col :span="11">
<el-input placeholder="请输入CIDR地址" v-model="item.val"> <el-input placeholder="请输入CIDR地址" v-model="item.val">
<el-select v-model="item.action" slot="prepend"> <el-select v-model="item.action" slot="prepend">
@ -262,9 +266,13 @@
<el-col :span="3"> <el-col :span="3">
<el-input v-model.number="item.port" placeholder="端口"></el-input> <el-input v-model.number="item.port" placeholder="端口"></el-input>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="8">
<el-input v-model="item.note" placeholder="备注"></el-input> <el-input v-model="item.note" placeholder="备注"></el-input>
</el-col> </el-col>
<el-col :span="2">
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.link_acl,index)"></el-button>
</el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -389,13 +397,16 @@ export default {
console.log(error); console.log(error);
}); });
}, },
removeDomain(arr, item) { removeDomain(arr, index) {
console.log(item) console.log(index)
if (index >= 0 && index < arr.length) {
arr.splice(index, 1)
}
// let index = arr.indexOf(item); // let index = arr.indexOf(item);
// if (index !== -1 && arr.length > 1) { // if (index !== -1 && arr.length > 1) {
// arr.splice(index, 1) // arr.splice(index, 1)
// } // }
arr.pop() // arr.pop()
}, },
addDomain(arr) { addDomain(arr) {
arr.push({val: "", action: "allow", port: 0}); arr.push({val: "", action: "allow", port: 0});