mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-11-06 12:06:09 +08:00
init
This commit is contained in:
6
common/app_ver.go
Normal file
6
common/app_ver.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package common
|
||||
|
||||
const (
|
||||
APP_NAME = "AnyLink"
|
||||
APP_VER = "0.0.1"
|
||||
)
|
||||
10
common/assert_test.go
Normal file
10
common/assert_test.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package common
|
||||
|
||||
import "testing"
|
||||
|
||||
func AssertTrue(t *testing.T, a bool) {
|
||||
t.Helper()
|
||||
if !a {
|
||||
t.Errorf("Not True %t", a)
|
||||
}
|
||||
}
|
||||
77
common/cfg_server.go
Normal file
77
common/cfg_server.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
var (
|
||||
ServerCfg = &ServerConfig{}
|
||||
)
|
||||
|
||||
// # ReKey time (in seconds)
|
||||
// rekey-time = 172800
|
||||
// # ReKey method
|
||||
// # Valid options: ssl, new-tunnel
|
||||
// # ssl: Will perform an efficient rehandshake on the channel allowing
|
||||
// # a seamless connection during rekey.
|
||||
// # new-tunnel: Will instruct the client to discard and re-establish the channel.
|
||||
// # Use this option only if the connecting clients have issues with the ssl
|
||||
// # option.
|
||||
// rekey-method = ssl
|
||||
|
||||
type ServerConfig struct {
|
||||
UserFile string `toml:"user_file"`
|
||||
ServerAddr string `toml:"server_addr"`
|
||||
DebugAddr string `toml:"debug_addr"`
|
||||
CertFile string `toml:"cert_file"`
|
||||
CertKey string `toml:"cert_key"`
|
||||
LinkGroups []string `toml:"link_groups"`
|
||||
DefaultGroup string `toml:"default_group"`
|
||||
Banner string `toml:"banner"` // 欢迎语
|
||||
CstpDpd int `toml:"cstp_dpd"` // Dead peer detection in seconds
|
||||
CstpKeepalive int `toml:"cstp_keepalive"` // in seconds
|
||||
SessionTimeout int `toml:"session_timeout"` // in seconds
|
||||
AuthTimeout int `toml:"auth_timeout"` // in seconds
|
||||
MaxClient int `toml:"max_client"`
|
||||
MaxUserClient int `toml:"max_user_client"`
|
||||
Ipv4Network string `toml:"ipv4_network"` // 192.168.1.0
|
||||
Ipv4Netmask string `toml:"ipv4_netmask"` // 255.255.255.0
|
||||
Ipv4GateWay string `toml:"-"`
|
||||
Include []string `toml:"include"` // 10.10.10.0/255.255.255.0
|
||||
Exclude []string `toml:"exclude"` // 192.168.5.0/255.255.255.0
|
||||
ClientDns []string `toml:"client_dns"` // 114.114.114.114
|
||||
AllowLan bool `toml:"allow_lan"` // 允许本地LAN访问vpn网络
|
||||
}
|
||||
|
||||
func loadServer() {
|
||||
b, err := ioutil.ReadFile(serverFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = toml.Unmarshal(b, ServerCfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sf, _ := filepath.Abs(serverFile)
|
||||
base := filepath.Dir(sf)
|
||||
|
||||
// 转换成绝对路径
|
||||
ServerCfg.UserFile = getAbsPath(base, ServerCfg.UserFile)
|
||||
ServerCfg.CertFile = getAbsPath(base, ServerCfg.CertFile)
|
||||
ServerCfg.CertKey = getAbsPath(base, ServerCfg.CertKey)
|
||||
|
||||
fmt.Printf("ServerCfg: %+v \n", ServerCfg)
|
||||
}
|
||||
|
||||
func getAbsPath(base, cfile string) string {
|
||||
abs := filepath.IsAbs(cfile)
|
||||
if abs {
|
||||
return cfile
|
||||
}
|
||||
return filepath.Join(base, cfile)
|
||||
}
|
||||
91
common/cfg_user.go
Normal file
91
common/cfg_user.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
var (
|
||||
users = map[string]User{}
|
||||
limitClient = map[string]int{"_all": 0}
|
||||
limitMux = sync.Mutex{}
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Group string `toml:"group"`
|
||||
Username string `toml:"-"`
|
||||
Password string `toml:"password"`
|
||||
OtpSecret string `toml:"otp_secret"`
|
||||
}
|
||||
|
||||
func CheckUser(name, pwd, group string) bool {
|
||||
user, ok := users[name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
pwdHash := hashPass(pwd)
|
||||
if user.Password == pwdHash {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hashPass(pwd string) string {
|
||||
sum := sha1.Sum([]byte(pwd))
|
||||
return fmt.Sprintf("%x", sum)
|
||||
}
|
||||
|
||||
func LimitClient(name string, close bool) bool {
|
||||
limitMux.Lock()
|
||||
defer limitMux.Unlock()
|
||||
// defer fmt.Println(limitClient)
|
||||
|
||||
_all := limitClient["_all"]
|
||||
c, ok := limitClient[name]
|
||||
if !ok { // 不存在用户
|
||||
limitClient[name] = 0
|
||||
}
|
||||
|
||||
if close {
|
||||
limitClient[name] = c - 1
|
||||
limitClient["_all"] = _all - 1
|
||||
return true
|
||||
}
|
||||
|
||||
// 全局判断
|
||||
if _all >= ServerCfg.MaxClient {
|
||||
return false
|
||||
}
|
||||
|
||||
// 超出同一个用户限制
|
||||
if c >= ServerCfg.MaxUserClient {
|
||||
return false
|
||||
}
|
||||
|
||||
limitClient[name] = c + 1
|
||||
limitClient["_all"] = _all + 1
|
||||
return true
|
||||
}
|
||||
|
||||
func loadUser() {
|
||||
b, err := ioutil.ReadFile(ServerCfg.UserFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = toml.Unmarshal(b, &users)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 添加用户名
|
||||
for k, v := range users {
|
||||
v.Username = k
|
||||
users[k] = v
|
||||
}
|
||||
|
||||
fmt.Println("users:", users)
|
||||
}
|
||||
33
common/cfg_user_test.go
Normal file
33
common/cfg_user_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckUser(t *testing.T) {
|
||||
users["user1"] = User{Password: "7c4a8d09ca3762af61e59520943dc26494f8941b"}
|
||||
users["user2"] = User{Password: "7c4a8d09ca3762af61e59520943dc26494f8941c"}
|
||||
|
||||
var res bool
|
||||
res = CheckUser("user1", "123456", "")
|
||||
AssertTrue(t, res == true)
|
||||
|
||||
res = CheckUser("user2", "123457", "")
|
||||
AssertTrue(t, res == false)
|
||||
}
|
||||
|
||||
func TestLimitClient(t *testing.T) {
|
||||
ServerCfg.MaxClient = 2
|
||||
ServerCfg.MaxUserClient = 1
|
||||
|
||||
res1 := LimitClient("user1", false)
|
||||
res2 := LimitClient("user1", false)
|
||||
res3 := LimitClient("user2", false)
|
||||
res4 := LimitClient("user3", false)
|
||||
|
||||
AssertTrue(t, res1 == true)
|
||||
AssertTrue(t, res2 == false)
|
||||
AssertTrue(t, res3 == true)
|
||||
AssertTrue(t, res4 == false)
|
||||
|
||||
}
|
||||
44
common/flag.go
Normal file
44
common/flag.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
// 提交id
|
||||
CommitId string
|
||||
// 配置文件
|
||||
serverFile string
|
||||
passwd string
|
||||
// 显示版本信息
|
||||
rev bool
|
||||
)
|
||||
|
||||
func initFlag() {
|
||||
flag.StringVar(&serverFile, "conf", "./conf/server.toml", "server config file path")
|
||||
flag.StringVar(&passwd, "pass", "", "generation a sha1 password")
|
||||
flag.BoolVar(&rev, "rev", false, "display version info")
|
||||
flag.Parse()
|
||||
|
||||
if passwd != "" {
|
||||
pwdHash := hashPass(passwd)
|
||||
fmt.Printf("passwd-sha1:%s\n", pwdHash)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if rev {
|
||||
fmt.Printf("%s v%s build on %s [%s, %s] commit_id(%s) \n",
|
||||
APP_NAME, APP_VER, runtime.Version(), runtime.GOOS, runtime.GOARCH, CommitId)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
initFlag()
|
||||
loadServer()
|
||||
loadUser()
|
||||
initIpPool()
|
||||
}
|
||||
139
common/ip_pool.go
Normal file
139
common/ip_pool.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// ip租期 (秒)
|
||||
IpLease = 1209600
|
||||
)
|
||||
|
||||
var (
|
||||
ipPool = &IpPoolConfig{}
|
||||
macIps = map[string]*MacIp{}
|
||||
)
|
||||
|
||||
type MacIp struct {
|
||||
IsActive bool
|
||||
Ip net.IP
|
||||
MacAddr string
|
||||
LastLogin time.Time
|
||||
}
|
||||
|
||||
type IpPoolConfig struct {
|
||||
mux sync.Mutex
|
||||
// 计算动态ip
|
||||
Ipv4Net *net.IPNet
|
||||
Ipv4GateWay net.IP
|
||||
IpLongMin uint32
|
||||
IpLongMax uint32
|
||||
IpLongNow uint32
|
||||
}
|
||||
|
||||
func initIpPool() {
|
||||
// ip地址
|
||||
ip := net.ParseIP(ServerCfg.Ipv4Network)
|
||||
// 子网掩码
|
||||
maskIp := net.ParseIP(ServerCfg.Ipv4Netmask).To4()
|
||||
mask := net.IPMask(maskIp)
|
||||
|
||||
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||
ipPool.Ipv4Net = ipNet
|
||||
|
||||
// 网络地址零值
|
||||
min := binary.BigEndian.Uint32(ip.Mask(mask))
|
||||
// 广播地址
|
||||
one, _ := ipNet.Mask.Size()
|
||||
max := min | uint32(math.Pow(2, float64(32-one))-1)
|
||||
|
||||
min += 1 // 网关
|
||||
ipPool.Ipv4GateWay = long2ip(min)
|
||||
ServerCfg.Ipv4GateWay = ipPool.Ipv4GateWay.String()
|
||||
// 第一个可用地址
|
||||
min += 1
|
||||
ipPool.IpLongMin = min
|
||||
ipPool.IpLongMax = max
|
||||
ipPool.IpLongNow = min
|
||||
}
|
||||
|
||||
func long2ip(i uint32) net.IP {
|
||||
ip := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(ip, i)
|
||||
return ip
|
||||
}
|
||||
|
||||
// 获取动态ip
|
||||
func AcquireIp(macAddr string) net.IP {
|
||||
ipPool.mux.Lock()
|
||||
defer ipPool.mux.Unlock()
|
||||
tNow := time.Now()
|
||||
|
||||
// 判断已经分配过
|
||||
if mi, ok := macIps[macAddr]; ok {
|
||||
mi.IsActive = true
|
||||
mi.LastLogin = tNow
|
||||
return mi.Ip
|
||||
}
|
||||
|
||||
// ip池分配完之前
|
||||
if ipPool.IpLongNow < ipPool.IpLongMax {
|
||||
// 递增分配一个ip
|
||||
ip := long2ip(ipPool.IpLongNow)
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
ipPool.IpLongNow += 1
|
||||
return ip
|
||||
}
|
||||
|
||||
// 查找过期数据
|
||||
farMi := &MacIp{LastLogin: tNow}
|
||||
for k, v := range macIps {
|
||||
// 跳过活跃连接
|
||||
if v.IsActive {
|
||||
continue
|
||||
}
|
||||
|
||||
// 已经超过租期
|
||||
if tNow.Sub(v.LastLogin) > IpLease*time.Second {
|
||||
delete(macIps, k)
|
||||
ip := v.Ip
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
return ip
|
||||
}
|
||||
|
||||
// 其他情况判断最早登陆的mac
|
||||
if v.LastLogin.Before(farMi.LastLogin) {
|
||||
farMi = v
|
||||
}
|
||||
}
|
||||
|
||||
// 全都在线,没有数据可用
|
||||
if farMi.MacAddr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用最早登陆的mac地址
|
||||
delete(macIps, farMi.MacAddr)
|
||||
ip := farMi.Ip
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
return ip
|
||||
}
|
||||
|
||||
// 回收ip
|
||||
func ReleaseIp(ip net.IP, macAddr string) {
|
||||
ipPool.mux.Lock()
|
||||
defer ipPool.mux.Unlock()
|
||||
if mi, ok := macIps[macAddr]; ok {
|
||||
if mi.Ip.Equal(ip) {
|
||||
mi.IsActive = false
|
||||
mi.LastLogin = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
50
common/ip_pool_test.go
Normal file
50
common/ip_pool_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAcquireIp(t *testing.T) {
|
||||
ServerCfg.Ipv4Network = "192.168.1.0"
|
||||
ServerCfg.Ipv4Netmask = "255.255.255.0"
|
||||
macIps = map[string]*MacIp{}
|
||||
initIpPool()
|
||||
|
||||
var ip net.IP
|
||||
|
||||
for i := 2; i <= 100; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
ip = AcquireIp(fmt.Sprintf("mac-new"))
|
||||
AssertTrue(t, ip.Equal(net.IPv4(192, 168, 1, 101)))
|
||||
for i := 102; i <= 254; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
ip = AcquireIp(fmt.Sprintf("mac-nil"))
|
||||
AssertTrue(t, ip == nil)
|
||||
}
|
||||
|
||||
func TestReleaseIp(t *testing.T) {
|
||||
ServerCfg.Ipv4Network = "192.168.1.0"
|
||||
ServerCfg.Ipv4Netmask = "255.255.255.0"
|
||||
macIps = map[string]*MacIp{}
|
||||
initIpPool()
|
||||
|
||||
var ip net.IP
|
||||
|
||||
// 分配完所有数据
|
||||
for i := 2; i <= 254; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
|
||||
ip = AcquireIp(fmt.Sprintf("mac-more"))
|
||||
AssertTrue(t, ip == nil)
|
||||
|
||||
ReleaseIp(net.IPv4(192, 168, 1, 123), "mac-123")
|
||||
ReleaseIp(net.IPv4(192, 168, 1, 100), "mac-100")
|
||||
ip = AcquireIp(fmt.Sprintf("mac-new"))
|
||||
// 最早过期的ip
|
||||
AssertTrue(t, ip.Equal(net.IPv4(192, 168, 1, 123)))
|
||||
}
|
||||
7
common/log.go
Normal file
7
common/log.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package common
|
||||
|
||||
import "log"
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
Reference in New Issue
Block a user