代码同步至1.4.10

发布1.5.1
This commit is contained in:
古大羊 2022-10-26 18:38:27 +08:00
parent 5e820e6555
commit 24b820e67c
67 changed files with 5835 additions and 3565 deletions

3
.gitignore vendored
View File

@ -15,6 +15,9 @@ lucky
*.out
*.log
*.upx
*.pem
*.crt
*.key
lucky.conf
# Dependency directories (remove the comment below to include it)

View File

@ -9,9 +9,7 @@
- [后台界面](#后台界面)
- [开发编译](#开发编译)
- [更新日志](#更新日志)
- [使用注意与常见问题](#使用注意与常见问题)
<!-- /TOC -->
@ -42,7 +40,6 @@
- 支持的DNS服务商和DDNS-GO一样,有Alidns(阿里云),百度云,Cloudflare,Dnspod(腾讯云),华为云.自定义(Callback)内置有每步,No-IP,Dynv6,Dynu模版,一键填充,仅需修改相应用户密码或者token即可快速接入.
- 3.http/https反向代理
- 特点
- SSL证书管理简单
- 设置简单
- 支持HttpBasic认证
- 支持IP黑白名单
@ -50,6 +47,15 @@
- 日志记录最近访问情况
- 一键开关子规则
- 前端域名与后端地址 支持一对一,一对多(均衡负载),多对多(下一级反向代理)
- 4.网络唤醒
- 特点
- 支持远程控制唤醒和关机操作
- 远程唤醒需要 待唤醒端所在局域网内有开启中继唤醒指令的lucky唤醒客户端
- 远程关机需要 待关机端运行有luck唤醒客户端
- 支持接入第三方物联网平台(点灯科技 巴法云),可通过各大平台的语音助手控制设备唤醒和关机.
- 点灯科技支持 小爱同学 小度 天猫精灵
- 巴法云支持小爱同学 小度 天猫精灵 google语音 AmazonAlexa
- 具备但一般用不上的功能:支持一个设备设置多组网卡mac和多个广播地址,实现批量控制设备.
- 将要实现的功能
- 有建议可联系作者.
@ -135,11 +141,16 @@
#### Http反向代理
![](./previews/reverseproxy.png)
#### 网络唤醒
![](./previews/wol001.png)
![](./previews/wol002.png)
# 开发编译
#开发编译
```bash
@ -147,17 +158,9 @@
```
# 更新日志
2022-10-08 版本1.4.9
1.反向代理新增https支持
2.加入SSL证书管理模块
3.后台接口支持HTTPS
4.端口转发模块重构优化移除命令行配置功能,注意使用此版本后原来的端口转发配置会全部消失
5.修复已知BUG.
6.源码不再和二进制版本同时发布.
2022-10-14 v1.4.10
1.修复特定情况下反向代理规则添加后无法编辑删除的bug.
@ -171,5 +174,3 @@
- 转发规则启用异常,端口转发没有生效时请登录后台查看日志.
- 开启外网访问可以直接修改配置文件中的"AllowInternetaccess": false, 将false改为true

View File

@ -43,31 +43,30 @@ func FlushLoginRandomKey() {
loginRandomkey = stringsp.GetRandomString(16)
}
type ConfigureRelayRule struct {
Name string `json:"Name"`
Configurestr string `json:"Configurestr"`
Enable bool `json:"Enable"`
Options socketproxy.RelayRuleOptions `json:"Options"`
}
type BaseConfigure struct {
AdminWebListenPort int `json:"AdminWebListenPort"` //管理后台端口
ProxyCountLimit int64 `json:"ProxyCountLimit"` //全局代理数量限制
AdminAccount string `json:"AdminAccount"` //登录账号
AdminPassword string `json:"AdminPassword"` //登录密码
AllowInternetaccess bool `json:"AllowInternetaccess"` //允许外网访问
GlobalMaxConnections int64 `json:"GlobalMaxConnections"` //全局最大连接数
LogMaxSize int `json:"LogMaxSize"` //日志记录最大条数
AdminWebListenPort int `json:"AdminWebListenPort"` //管理后台端口
AdminWebListenTLS bool `json:"AdminWebListenTLS"` //启用TLS监听端口
AdminWebListenHttpsPort int `json:"AdminWebListenHttpsPort"` //管理后台Https端口
//ProxyCountLimit int64 `json:"ProxyCountLimit"` //全局代理数量限制
AdminAccount string `json:"AdminAccount"` //登录账号
AdminPassword string `json:"AdminPassword"` //登录密码
AllowInternetaccess bool `json:"AllowInternetaccess"` //允许外网访问
//GlobalMaxConnections int64 `json:"GlobalMaxConnections"` //全局最大连接数
LogMaxSize int `json:"LogMaxSize"` //日志记录最大条数
}
type ProgramConfigure struct {
BaseConfigure BaseConfigure `json:"BaseConfigure"`
RelayRuleList []ConfigureRelayRule `json:"RelayRuleList"`
WhiteListConfigure WhiteListConfigure `json:"WhiteListConfigure"`
BlackListConfigure BlackListConfigure `json:"BlackListConfigure"`
DDNSConfigure DDNSConfigure `json:"DDNSConfigure"` //DDNS 参数设置
DDNSTaskList []DDNSTask `json:"DDNSTaskList"` //DDNS任务列表
ReverseProxyRuleList []ReverseProxyRule `json:"ReverseProxyRuleList"` //反向代理规则列表
BaseConfigure BaseConfigure `json:"BaseConfigure"`
WhiteListConfigure WhiteListConfigure `json:"WhiteListConfigure"`
BlackListConfigure BlackListConfigure `json:"BlackListConfigure"`
DDNSConfigure DDNSConfigure `json:"DDNSConfigure"` //DDNS 参数设置
DDNSTaskList []DDNSTask `json:"DDNSTaskList"` //DDNS任务列表
ReverseProxyRuleList []ReverseProxyRule `json:"ReverseProxyRuleList"` //反向代理规则列表
SSLCertficateList []SSLCertficate `json:"SSLCertficateList"` //SSL证书列表
PortForwardsRuleList []PortForwardsRule `json:"PortForwardsRuleList"` //端口转发规则列表
PortForwardsConfigure PortForwardsConfigure `json:"PortForwardsConfigure"` //端口转发设置
WOLDeviceList []WOLDevice `json:"WOLDeviceList"` //网络唤醒设备列表
}
var programConfigureMutex sync.RWMutex
@ -90,12 +89,6 @@ func GetAuthAccount() map[string]string {
return accountInfo
}
func SetConfigRuleList(ruleList *[]ConfigureRelayRule) {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
programConfigure.RelayRuleList = *ruleList
}
func GetRunMode() string {
return runMode
}
@ -146,14 +139,51 @@ func GetDDNSConfigure() DDNSConfigure {
return conf
}
func GetPortForwardsConfigure() PortForwardsConfigure {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
conf := programConfigure.PortForwardsConfigure
return conf
}
func SetPortForwardsConfigure(conf *PortForwardsConfigure) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
if conf.PortForwardsLimit < 0 {
conf.PortForwardsLimit = 0
} else if conf.PortForwardsLimit > 1024 {
conf.PortForwardsLimit = 1024
}
if conf.TCPPortforwardMaxConnections < 0 {
conf.TCPPortforwardMaxConnections = 0
} else if conf.TCPPortforwardMaxConnections > 4096 {
conf.TCPPortforwardMaxConnections = 4096
}
if conf.UDPReadTargetDataMaxgoroutineCount < 0 {
conf.UDPReadTargetDataMaxgoroutineCount = 0
} else if conf.UDPReadTargetDataMaxgoroutineCount > 4096 {
conf.UDPReadTargetDataMaxgoroutineCount = 4096
}
programConfigure.PortForwardsConfigure = *conf
socketproxy.SetGlobalMaxPortForwardsCountLimit(conf.PortForwardsLimit)
socketproxy.SetGlobalTCPPortforwardMaxConnections(conf.TCPPortforwardMaxConnections)
socketproxy.SetGlobalUDPReadTargetDataMaxgoroutineCountLimit(conf.UDPReadTargetDataMaxgoroutineCount)
return Save()
}
// 保存基础配置
func SetBaseConfigure(conf *BaseConfigure) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
programConfigure.BaseConfigure = *conf
socketproxy.SetGlobalMaxConnections(conf.GlobalMaxConnections)
socketproxy.SetGlobalMaxProxyCount(conf.ProxyCountLimit)
//socketproxy.SetGlobalMaxConnections(conf.GlobalMaxConnections)
//socketproxy.SetGlobalMaxPortForwardsCount(conf.ProxyCountLimit)
if conf.LogMaxSize < minLogSize {
conf.LogMaxSize = minLogSize
@ -200,18 +230,29 @@ func Read(filePath string) (err error) {
return err
}
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
pc.BaseConfigure.GlobalMaxConnections = socketproxy.DEFAULT_GLOBAL_MAX_CONNECTIONS
if pc.PortForwardsConfigure.PortForwardsLimit <= 0 {
pc.PortForwardsConfigure.PortForwardsLimit = socketproxy.DEFAULT_MAX_PORTFORWARDS_LIMIT
}
if pc.PortForwardsConfigure.TCPPortforwardMaxConnections <= 0 {
pc.PortForwardsConfigure.TCPPortforwardMaxConnections = socketproxy.DEFAULT_GLOBAL_MAX_CONNECTIONS
}
if pc.BaseConfigure.ProxyCountLimit <= 0 {
pc.BaseConfigure.ProxyCountLimit = socketproxy.DEFAULT_MAX_PROXY_COUNT
if pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount <= 0 {
pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount = socketproxy.DEFAULT_GLOBAL_UDPReadTargetDataMaxgoroutineCount
}
socketproxy.SetGlobalMaxPortForwardsCountLimit(pc.PortForwardsConfigure.PortForwardsLimit)
socketproxy.SetGlobalTCPPortforwardMaxConnections(pc.PortForwardsConfigure.TCPPortforwardMaxConnections)
socketproxy.SetGlobalUDPReadTargetDataMaxgoroutineCountLimit(pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount)
if pc.BaseConfigure.AdminWebListenPort <= 0 {
pc.BaseConfigure.AdminWebListenPort = 16601
}
if pc.BaseConfigure.AdminWebListenHttpsPort <= 0 {
pc.BaseConfigure.AdminWebListenHttpsPort = 16626
}
if pc.BaseConfigure.LogMaxSize < minLogSize {
pc.BaseConfigure.LogMaxSize = minLogSize
} else if pc.BaseConfigure.LogMaxSize > maxLogSize {
@ -223,11 +264,8 @@ func Read(filePath string) (err error) {
return nil
}
func LoadDefault(proxyCountLimit int64,
adminWebListenPort int,
globalMaxConnections int64) {
programConfigure = loadDefaultConfigure(proxyCountLimit, adminWebListenPort, globalMaxConnections)
func LoadDefault(adminWebListenPort int) {
programConfigure = loadDefaultConfigure(adminWebListenPort)
}
func Save() (err error) {
@ -281,17 +319,13 @@ func readProgramConfigure(filePath string) (conf *ProgramConfigure, err error) {
}
func loadDefaultConfigure(
proxyCountLimit int64,
adminWebListenPort int,
globalMaxConnections int64) *ProgramConfigure {
adminWebListenPort int) *ProgramConfigure {
baseConfigure := BaseConfigure{AdminWebListenPort: adminWebListenPort,
GlobalMaxConnections: globalMaxConnections,
AdminAccount: defaultAdminAccount,
AdminPassword: defaultAdminPassword,
ProxyCountLimit: proxyCountLimit,
AllowInternetaccess: false,
LogMaxSize: defaultLogSize}
AdminAccount: defaultAdminAccount,
AdminPassword: defaultAdminPassword,
AllowInternetaccess: false,
LogMaxSize: defaultLogSize}
whiteListConfigure := WhiteListConfigure{BaseConfigure: WhiteListBaseConfigure{ActivelifeDuration: 36, BasicAccount: defaultAdminAccount, BasicPassword: defaultAdminPassword}}
@ -299,13 +333,21 @@ func loadDefaultConfigure(
pc.BaseConfigure = baseConfigure
pc.WhiteListConfigure = whiteListConfigure
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
pc.BaseConfigure.GlobalMaxConnections = socketproxy.DEFAULT_GLOBAL_MAX_CONNECTIONS
if pc.PortForwardsConfigure.PortForwardsLimit <= 0 {
pc.PortForwardsConfigure.PortForwardsLimit = socketproxy.DEFAULT_MAX_PORTFORWARDS_LIMIT
}
socketproxy.SetGlobalMaxPortForwardsCountLimit(pc.PortForwardsConfigure.PortForwardsLimit)
if pc.PortForwardsConfigure.TCPPortforwardMaxConnections <= 0 {
pc.PortForwardsConfigure.TCPPortforwardMaxConnections = socketproxy.TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS
}
socketproxy.SetGlobalTCPPortforwardMaxConnections(pc.PortForwardsConfigure.TCPPortforwardMaxConnections)
if pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount <= 0 {
pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount = socketproxy.DEFAULT_GLOBAL_UDPReadTargetDataMaxgoroutineCount
}
if pc.BaseConfigure.ProxyCountLimit <= 0 {
pc.BaseConfigure.ProxyCountLimit = socketproxy.DEFAULT_MAX_PROXY_COUNT
}
socketproxy.SetGlobalUDPReadTargetDataMaxgoroutineCountLimit(pc.PortForwardsConfigure.UDPReadTargetDataMaxgoroutineCount)
if pc.BaseConfigure.AdminWebListenPort <= 0 {
pc.BaseConfigure.AdminWebListenPort = defaultAdminListenPort

View File

@ -57,6 +57,7 @@ type DNSConfig struct {
ForceInterval int `json:"ForceInterval"` //(秒)即使IP没有变化,到一定时间后依然强制更新或先DNS解析比较IP再更新
ResolverDoaminCheck bool `json:"ResolverDoaminCheck"` //调用callback同步前先解析一次域名,如果IP相同就不同步
DNSServerList []string `json:"DNSServerList"` //DNS服务器列表
CallAPINetwork string `json:"CallAPINetwork"` //空代理tcp, tcp4,tcp6
Callback DNSCallback `json:"Callback"`
HttpClientProxyType string `json:"HttpClientProxyType"` //http client代理服务器设置
HttpClientProxyAddr string `json:"HttpClientProxyAddr"` //代理服务器IP
@ -64,6 +65,15 @@ type DNSConfig struct {
HttpClientProxyPassword string `json:"HttpClientProxyPassword"` //代理密码
}
func (d *DNSConfig) GetCallAPINetwork() string {
switch d.CallAPINetwork {
case "tcp4", "tcp6":
return d.CallAPINetwork
default:
return "tcp"
}
}
type DNSCallback struct {
URL string `json:"URL"` //请求地址
Method string `json:"Method"` //请求方法
@ -179,8 +189,8 @@ func GetDDNSTaskConfigureList() []*DDNSTask {
}
func GetDDNSTaskByKey(taskKey string) *DDNSTask {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
taskIndex := -1
for i := range programConfigure.DDNSTaskList {

452
config/portforward.go Normal file
View File

@ -0,0 +1,452 @@
package config
import (
"fmt"
"log"
"strconv"
"strings"
"github.com/gdy666/lucky/socketproxy"
"github.com/gdy666/lucky/thirdlib/gdylib/logsbuffer"
"github.com/sirupsen/logrus"
)
type PortForwardsConfigure struct {
PortForwardsLimit int64 `json:"PortForwardsLimit"` //全局端口转发数量限制
TCPPortforwardMaxConnections int64 `json:"TCPPortforwardMaxConnections"` //端口转发全局TCP并发链接数限制
UDPReadTargetDataMaxgoroutineCount int64 `json:"UDPReadTargetDataMaxgoroutineCount"` //端口转发全局UDP读取目标地址数据协程数限制
}
type PortForwardsRule struct {
Name string `json:"Name"`
Key string `json:"Key"`
Enable bool `json:"Enable"`
ForwardTypes []string `json:"ForwardTypes"`
ListenAddress string `json:"ListenAddress"`
ListenPorts string `json:"ListenPorts"`
TargetAddressList []string `json:"TargetAddressList"`
TargetPorts string `json:"TargetPorts"`
Options socketproxy.RelayRuleOptions `json:"Options"`
ReverseProxyList *[]socketproxy.Proxy `json:"-"`
logsBuffer *logsbuffer.LogsBuffer
logrus *logrus.Logger
LogLevel int `json:"LogLevel"` //日志输出级别
LogOutputToConsole bool `json:"LogOutputToConsole"` //日志输出到终端
AccessLogMaxNum int `json:"AccessLogMaxNum"`
WebListShowLastLogMaxCount int `json:"WebListShowLastLogMaxCount"` //前端列表显示最新日志最大条数
}
func (r *PortForwardsRule) ProxyCount() int {
if r.ReverseProxyList == nil {
return 0
}
return len(*r.ReverseProxyList)
}
func (r *PortForwardsRule) StartAllProxys() {
if r.ReverseProxyList == nil {
return
}
for i := range *r.ReverseProxyList {
(*r.ReverseProxyList)[i].StartProxy()
}
}
func (r *PortForwardsRule) GetLastLogs(maxCount int) []any {
return r.GetLogsBuffer().GetLastLogs(WebLogConvert, maxCount)
}
func (r *PortForwardsRule) Fire(entry *logrus.Entry) error {
if !r.LogOutputToConsole {
return nil
}
s, _ := entry.String()
log.Print(s)
return nil
}
func (r *PortForwardsRule) Levels() []logrus.Level {
return logrus.AllLevels
}
func (r *PortForwardsRule) GetLogrus() *logrus.Logger {
if r.logrus == nil {
r.logrus = logrus.New()
r.logrus.SetLevel(logrus.Level(r.LogLevel))
r.logrus.SetOutput(r.GetLogsBuffer())
r.logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
DisableTimestamp: true,
DisableHTMLEscape: true,
DataKey: "ExtInfo",
})
r.logrus.AddHook(r)
}
return r.logrus
}
func (r *PortForwardsRule) GetLogsBuffer() *logsbuffer.LogsBuffer {
if r.logsBuffer == nil {
r.logsBuffer = logsbuffer.CreateLogbuffer("portforward:"+r.Key, r.AccessLogMaxNum)
}
return r.logsBuffer
}
func (r *PortForwardsRule) StopAllProxys() {
if r.ReverseProxyList == nil {
return
}
for i := range *r.ReverseProxyList {
(*r.ReverseProxyList)[i].StopProxy()
}
}
// TidyReverseProxyCache 整理端口转发日志缓存
func TidyPortforwardLogsCache() {
ruleList := GetPortForwardsRuleList()
var keyListBuffer strings.Builder
for _, rule := range ruleList {
keyListBuffer.WriteString(rule.Key)
keyListBuffer.WriteString(",")
}
keyListStr := keyListBuffer.String()
logsbuffer.LogsBufferStoreMu.Lock()
defer logsbuffer.LogsBufferStoreMu.Unlock()
var needDeleteKeys []string
for k := range logsbuffer.LogsBufferStore {
if !strings.HasPrefix(k, "portforward:") {
continue
}
if len(k) <= 13 {
continue
}
if !strings.Contains(keyListStr, k[12:]) {
needDeleteKeys = append(needDeleteKeys, k)
}
}
for i := range needDeleteKeys {
delete(logsbuffer.LogsBufferStore, needDeleteKeys[i])
}
}
func (r *PortForwardsRule) InitProxyList() error {
listenPorts, err := PortsStrToIList(r.ListenPorts)
if err != nil {
return err
}
targetPorts, err := PortsStrToIList(r.TargetPorts)
if err != nil {
return err
}
if len(listenPorts) != len(targetPorts) {
return fmt.Errorf("端口个数不一致")
}
var proxyList []socketproxy.Proxy
for i := range r.ForwardTypes {
for j := range listenPorts {
p, err := socketproxy.CreateProxy(r.GetLogrus(), r.ForwardTypes[i],
r.ListenAddress,
r.TargetAddressList,
listenPorts[j],
targetPorts[j],
&r.Options)
if err != nil {
return err
}
proxyList = append(proxyList, p)
}
}
r.ReverseProxyList = &proxyList
return nil
}
func PortsCheck(ports1Str, ports2Str string) (bool, error) {
ports1, err := PortsStrToIList(ports1Str)
if err != nil {
return false, err
}
ports2, err := PortsStrToIList(ports2Str)
if err != nil {
return false, err
}
if len(ports1) != len(ports2) {
return false, fmt.Errorf("端口个数不一致")
}
return true, nil
}
// portsStrToIList
func PortsStrToIList(portsStr string) (ports []int, err error) {
if portsStr == "" {
return
}
if strings.Contains(portsStr, ",") {
tmpStrList := strings.Split(portsStr, ",")
for i := range tmpStrList {
tps, e := PortsStrToIList(tmpStrList[i])
if e != nil {
err = fmt.Errorf("端口字符串处理出错:%s", e.Error())
return
}
ports = append(ports, tps...)
}
return
}
portsStrList := strings.Split(portsStr, "-")
if len(portsStrList) > 2 {
err = fmt.Errorf("端口%s格式有误", portsStr)
return
}
if len(portsStrList) == 1 { //single listen port
listenPort, e := portStrToi(portsStrList[0])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", e.Error())
return
}
ports = append(ports, listenPort)
}
if len(portsStrList) == 2 {
minListenPort, e := portStrToi(portsStrList[0])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", portsStrList[0])
return
}
maxListenPort, e := portStrToi(portsStrList[1])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", portsStrList[1])
return
}
if maxListenPort <= minListenPort {
err = fmt.Errorf("前一个端口[%d]要小于后一个端口[%d]", minListenPort, maxListenPort)
return
}
i := minListenPort
for {
if i > maxListenPort {
break
}
ports = append(ports, i)
i++
}
}
return
}
func portStrToi(portStr string) (int, error) {
port, err := strconv.Atoi(portStr)
if err != nil {
return 0, fmt.Errorf("端口格式有误:%s", err.Error())
}
if port < 1 || port > 65535 {
return 0, fmt.Errorf("端口[%d]超出范围", port)
}
return port, nil
}
func PortForwardsRuleListInit() {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var err error
for i := range programConfigure.PortForwardsRuleList {
err = programConfigure.PortForwardsRuleList[i].InitProxyList()
if err != nil {
log.Printf("InitProxyList error:%s\n", err.Error())
}
if programConfigure.PortForwardsRuleList[i].Enable {
programConfigure.PortForwardsRuleList[i].StartAllProxys()
}
}
}
func GetPortForwardsRuleList() []PortForwardsRule {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var resList []PortForwardsRule
for i := range programConfigure.PortForwardsRuleList {
r := programConfigure.PortForwardsRuleList[i]
resList = append(resList, r)
}
return resList
}
func GetPortForwardsGlobalProxyCount() int {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
count := 0
for i := range programConfigure.PortForwardsRuleList {
count += programConfigure.PortForwardsRuleList[i].ProxyCount()
}
return count
}
func GetPortForwardsGlobalProxyCountExcept(key string) int {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
count := 0
for i := range programConfigure.PortForwardsRuleList {
if key == programConfigure.PortForwardsRuleList[i].Key {
continue
}
count += programConfigure.PortForwardsRuleList[i].ProxyCount()
}
return count
}
func GetPortForwardsRuleByKey(key string) *PortForwardsRule {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
index := -1
for i := range programConfigure.PortForwardsRuleList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return nil
}
res := programConfigure.PortForwardsRuleList[index]
return &res
}
func StopAllSocketProxysByRuleKey(key string) error {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
index := -1
for i := range programConfigure.PortForwardsRuleList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return fmt.Errorf("找不到key:%s对应的规则", key)
}
programConfigure.PortForwardsRuleList[index].StopAllProxys()
return nil
}
func StartAllSocketProxysByRuleKey(key string) error {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
index := -1
for i := range programConfigure.PortForwardsRuleList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return fmt.Errorf("找不到key:%s对应的规则", key)
}
programConfigure.PortForwardsRuleList[index].StartAllProxys()
return nil
}
func PortForwardsRuleListAdd(r *PortForwardsRule) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
r.Enable = true
programConfigure.PortForwardsRuleList = append(programConfigure.PortForwardsRuleList, *r)
return Save()
}
func PortForwardsRuleListDelete(key string) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.PortForwardsRuleList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return fmt.Errorf("找不到需要删除的端口转发规则")
}
programConfigure.PortForwardsRuleList[index].StopAllProxys()
programConfigure.PortForwardsRuleList = DeletePortForwardsRuleListSlice(programConfigure.PortForwardsRuleList, index)
return Save()
}
func EnablePortForwardsRuleByKey(key string, enable bool) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.DDNSTaskList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return fmt.Errorf("开关端口转发规则失败,key查找失败")
}
if enable {
programConfigure.PortForwardsRuleList[index].StartAllProxys()
} else {
programConfigure.PortForwardsRuleList[index].StopAllProxys()
}
programConfigure.PortForwardsRuleList[index].Enable = enable
return Save()
}
func UpdatePortForwardsRuleToPortForwardsRuleList(key string, r *PortForwardsRule) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.PortForwardsRuleList {
if programConfigure.PortForwardsRuleList[i].Key == key {
index = i
break
}
}
if index == -1 {
return fmt.Errorf("找不到需要更新的端口转发规则")
}
programConfigure.PortForwardsRuleList[index] = *r
return Save()
}
func DeletePortForwardsRuleListSlice(a []PortForwardsRule, deleteIndex int) []PortForwardsRule {
j := 0
for i := range a {
if i != deleteIndex {
a[j] = a[i]
j++
}
}
return a[:j]
}

View File

@ -1,6 +1,7 @@
package config
import (
"crypto/tls"
"fmt"
"log"
"net"
@ -19,30 +20,11 @@ import (
"github.com/sirupsen/logrus"
)
var reverseProxyLogsStore map[string]*logsbuffer.LogsBuffer
var reverseProxyLogsStoreMu sync.Mutex
var reverseProxyServerStore sync.Map
var reverseProxyServerStoreMu sync.Mutex
func init() {
reverseProxyLogsStore = make(map[string]*logsbuffer.LogsBuffer)
}
func CreateReverseProxyLogbuffer(key string, buffSize int) *logsbuffer.LogsBuffer {
reverseProxyLogsStoreMu.Lock()
defer reverseProxyLogsStoreMu.Unlock()
var buf *logsbuffer.LogsBuffer
var ok bool
if buf, ok = reverseProxyLogsStore[key]; !ok {
buf = &logsbuffer.LogsBuffer{}
buf.SetBufferSize(buffSize)
reverseProxyLogsStore[key] = buf
} else if buf.GetBufferSize() != buffSize {
buf.SetBufferSize(buffSize)
}
return buf
}
// TidyReverseProxyCache 整理反向代理日志缓存
@ -59,18 +41,26 @@ func TidyReverseProxyCache() {
}
keyListStr := keyListBuffer.String()
reverseProxyLogsStoreMu.Lock()
defer reverseProxyLogsStoreMu.Unlock()
logsbuffer.LogsBufferStoreMu.Lock()
defer logsbuffer.LogsBufferStoreMu.Unlock()
var needDeleteKeys []string
for k := range reverseProxyLogsStore {
if !strings.Contains(keyListStr, k) {
for k := range logsbuffer.LogsBufferStore {
if !strings.HasPrefix(k, "reverseproxy:") {
continue
}
if len(k) <= 13 {
continue
}
if !strings.Contains(keyListStr, k[13:]) {
needDeleteKeys = append(needDeleteKeys, k)
}
}
for i := range needDeleteKeys {
delete(reverseProxyLogsStore, needDeleteKeys[i])
delete(logsbuffer.LogsBufferStore, needDeleteKeys[i])
reverseProxyServerStore.Delete(needDeleteKeys[i])
}
@ -85,12 +75,11 @@ type SubReverProxyRule struct {
locationsCount int `json:"-"`
locationIndex uint64 `json:"-"`
EnableAccessLog bool `json:"EnableAccessLog"` //开启日志
LogLevel int `json:"LogLevel"` //日志输出级别
LogOutputToConsole bool `json:"LogOutputToConsole"` //日志输出到终端
AccessLogMaxNum int `json:"AccessLogMaxNum"` //最大条数
WebListShowLastLogMaxCount int `json:"WebListShowLastLogMaxCount"` //前端列表显示最新日志最大条数
RequestInfoLogFormat string `json:"RequestInfoLogFormat"` //请求信息在日志中的格式
EnableAccessLog bool `json:"EnableAccessLog"` //开启日志
LogLevel int `json:"LogLevel"` //日志输出级别
LogOutputToConsole bool `json:"LogOutputToConsole"` //日志输出到终端
AccessLogMaxNum int `json:"AccessLogMaxNum"` //最大条数
WebListShowLastLogMaxCount int `json:"WebListShowLastLogMaxCount"` //前端列表显示最新日志最大条数
ForwardedByClientIP bool `json:"ForwardedByClientIP"`
TrustedCIDRsStrList []string `json:"TrustedCIDRsStrList"`
@ -163,12 +152,13 @@ func (r *SubReverProxyRule) Logf(level logrus.Level, c *gin.Context, format stri
}).Logf(level, format, v...)
}
func (r *SubReverProxyRule) HandlerReverseProxy(remote *url.URL, path string, c *gin.Context) {
func (r *SubReverProxyRule) HandlerReverseProxy(remote *url.URL, host, path string, c *gin.Context) {
proxy := httputil.NewSingleHostReverseProxy(remote)
proxy.Director = func(req *http.Request) {
req.Header = c.Request.Header
req.Host = remote.Host
req.Host = host //remote.Host
//req.Host = remote.Host
req.URL.Scheme = remote.Scheme
req.URL.Host = remote.Host
req.URL.Path = path
@ -182,21 +172,23 @@ func (r *SubReverProxyRule) HandlerReverseProxy(remote *url.URL, path string, c
}
func (r *SubReverProxyRule) PrintfToConsole(entry *logrus.Entry) error {
func (r *SubReverProxyRule) Fire(entry *logrus.Entry) error {
if !r.LogOutputToConsole {
return nil
}
s, _ := entry.String()
log.Print(s)
return nil
}
func (r *SubReverProxyRule) Levels() []logrus.Level {
return logrus.AllLevels
}
func (r *SubReverProxyRule) GetLogrus() *logrus.Logger {
if r.logrus == nil {
r.logrus = logrus.New()
r.logrus.SetLevel(logrus.Level(r.LogLevel))
r.GetLogsBuffer().SetFireCallback(r.PrintfToConsole)
r.logrus.SetOutput(r.GetLogsBuffer())
r.logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
@ -204,8 +196,7 @@ func (r *SubReverProxyRule) GetLogrus() *logrus.Logger {
DisableHTMLEscape: true,
DataKey: "ExtInfo",
})
r.logrus.AddHook(r.GetLogsBuffer())
r.logrus.AddHook(r)
}
return r.logrus
@ -220,7 +211,7 @@ func (r *SubReverProxyRule) GetLogger() *log.Logger {
func (r *SubReverProxyRule) GetLogsBuffer() *logsbuffer.LogsBuffer {
if r.logsBuffer == nil {
r.logsBuffer = CreateReverseProxyLogbuffer(r.Key, r.AccessLogMaxNum)
r.logsBuffer = logsbuffer.CreateLogbuffer("reverseproxy:"+r.Key, r.AccessLogMaxNum)
}
return r.logsBuffer
}
@ -313,7 +304,7 @@ func (r *ReverseProxyRule) ReverseProxyHandler(c *gin.Context) {
c.JSON(http.StatusBadGateway, gin.H{"ret": 1, "msg": fmt.Sprintf("后端地址[%s] 转换出错:%s", location, err.Error())})
return
}
subRule.HandlerReverseProxy(remote, path, c)
subRule.HandlerReverseProxy(remote, hostName, path, c)
}
@ -390,6 +381,18 @@ func (r *ReverseProxyRule) ServerStart() error {
Handler: ginR,
}
//***************************
var err error
server.TLSConfig = &tls.Config{}
if r.EnableTLS {
certList := GetValidSSLCertficateList()
server.TLSConfig.Certificates = certList
}
//server.TLSConfig.Certificates = make([]tls.Certificate, 3)
//****************************
ln, err := net.Listen(r.Network, r.Addr())
if err != nil {
return err
@ -398,14 +401,26 @@ func (r *ReverseProxyRule) ServerStart() error {
var serveResult error
go func() {
serveResult = server.Serve(ln)
if !r.EnableTLS {
serveResult = server.Serve(ln)
return
}
if len(server.TLSConfig.Certificates) <= 0 {
log.Printf("可用证书列表为空,%s 未能启用TLS", r.Addr())
serveResult = server.Serve(ln)
return
}
log.Printf("%s 已启用TLS", r.Addr())
serveResult = server.ServeTLS(ln, "", "")
}()
<-time.After(time.Millisecond * 300)
defer func() {
if serveResult == nil {
//setPreReverseProxyHttpServer(r.RuleKey, r.server)
r.SetServer(server)
}
}()
@ -561,7 +576,7 @@ type LogItem struct {
}
// 2006-01-02 15:04:05
func ReverseProxyLogConvert(lg *logsbuffer.LogItem) any {
func WebLogConvert(lg *logsbuffer.LogItem) any {
l := LogItem{
LogContent: lg.Content,
LogTime: time.Unix(lg.Timestamp/int64(time.Second), 0).Format("2006-01-02 15:04:05")}
@ -570,11 +585,11 @@ func ReverseProxyLogConvert(lg *logsbuffer.LogItem) any {
func (r *ReverseProxyRule) GetLastLogs() map[string][]any {
res := make(map[string][]any)
res["default"] = r.DefaultProxy.GetLogsBuffer().GetLastLogs(ReverseProxyLogConvert, r.DefaultProxy.WebListShowLastLogMaxCount)
res["default"] = r.DefaultProxy.GetLogsBuffer().GetLastLogs(WebLogConvert, r.DefaultProxy.WebListShowLastLogMaxCount)
for i := range r.ProxyList {
res[r.ProxyList[i].Key] = r.ProxyList[i].GetLogsBuffer().GetLastLogs(
ReverseProxyLogConvert, r.ProxyList[i].WebListShowLastLogMaxCount)
WebLogConvert, r.ProxyList[i].WebListShowLastLogMaxCount)
}
return res
}
@ -601,6 +616,7 @@ func GetReverseProxyRuleByKey(ruleKey string) *ReverseProxyRule {
ruleIndex := -1
for i := range programConfigure.ReverseProxyRuleList {
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
ruleIndex = i
break
@ -609,6 +625,7 @@ func GetReverseProxyRuleByKey(ruleKey string) *ReverseProxyRule {
if ruleIndex == -1 {
return nil
}
res := programConfigure.ReverseProxyRuleList[ruleIndex]
return &res
}
@ -635,7 +652,7 @@ func ReverseProxyRuleListDelete(ruleKey string) error {
}
if ruleIndex == -1 {
return fmt.Errorf("找不到需要删除的DDNS任务")
return fmt.Errorf("找不到需要删除的反向代理任务")
}
programConfigure.ReverseProxyRuleList = DeleteReverseProxyRuleListlice(programConfigure.ReverseProxyRuleList, ruleIndex)
@ -647,7 +664,7 @@ func EnableReverseProxyRuleByKey(ruleKey string, enable bool) error {
defer programConfigureMutex.Unlock()
ruleIndex := -1
for i := range programConfigure.DDNSTaskList {
for i := range programConfigure.ReverseProxyRuleList {
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
ruleIndex = i
break
@ -666,7 +683,7 @@ func EnableReverseProxySubRule(ruleKey, proxyKey string, enable bool) error {
defer programConfigureMutex.Unlock()
ruleIndex := -1
for i := range programConfigure.DDNSTaskList {
for i := range programConfigure.ReverseProxyRuleList {
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
ruleIndex = i
break
@ -699,7 +716,7 @@ func UpdateReverseProxyRulet(rule ReverseProxyRule) error {
defer programConfigureMutex.Unlock()
ruleIndex := -1
for i := range programConfigure.DDNSTaskList {
for i := range programConfigure.ReverseProxyRuleList {
if programConfigure.ReverseProxyRuleList[i].RuleKey == rule.RuleKey {
ruleIndex = i
break

289
config/sslcertficate.go Normal file
View File

@ -0,0 +1,289 @@
package config
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"log"
"strings"
"time"
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
)
type SSLCertficate struct {
Key string `json:"Key"`
Enable bool `json:"Enable"`
Remark string `json:"Remark"` //备注
CertBase64 string `json:"CertBase64"`
KeyBase64 string `json:"KeyBase64"`
AddTime string `json:"AddTime"` //添加时间
CertsInfo *[]CertInfo `json:"-"`
//---------------------
Certificate *tls.Certificate `json:"-"`
}
func (s *SSLCertficate) Init() error {
tc, err := CreateX509KeyPairByBase64Str(s.CertBase64, s.KeyBase64)
if err != nil {
return fmt.Errorf("CreateX509KeyPairByBase64Str error:%s", err.Error())
}
domainsInfo, err := GetCertDomainInfo(tc)
if err != nil {
return fmt.Errorf("GetCertDomainInfo error:%s", err.Error())
}
s.Certificate = tc
s.CertsInfo = domainsInfo
return nil
}
// GetOnlyDomain 返回证书第一条域名
func (s *SSLCertficate) GetFirstDomain() string {
if s.CertsInfo == nil {
return ""
}
if len(*s.CertsInfo) <= 0 {
return ""
}
if len((*s.CertsInfo)[0].Domains) <= 0 {
return ""
}
return (*s.CertsInfo)[0].Domains[0]
}
func CreateX509KeyPairByBase64Str(certBase64, keyBase64 string) (*tls.Certificate, error) {
crtBytes, err := base64.StdEncoding.DecodeString(certBase64)
if err != nil {
return nil, fmt.Errorf("certBase64 decode error:%s", err.Error())
}
keyBytes, err := base64.StdEncoding.DecodeString(keyBase64)
if err != nil {
return nil, fmt.Errorf("keyBase64 decode error:%s", err.Error())
}
cert, err := tls.X509KeyPair(crtBytes, keyBytes)
if err != nil {
return nil, fmt.Errorf("create X509KeyPair error:%s", err.Error())
}
return &cert, nil
}
type CertInfo struct {
Domains []string
NotBeforeTime string `json:"NotBeforeTime"` //time.Time
NotAfterTime string `json:"NotAfterTime"` //time.Time
}
func GetCertDomainInfo(cert *tls.Certificate) (*[]CertInfo, error) {
if cert == nil {
return nil, fmt.Errorf("cert == nil")
}
var res []CertInfo
for i := range cert.Certificate {
xx, err := x509.ParseCertificate(cert.Certificate[i])
if err != nil {
continue
}
ds := GetDomainsTrimSpace(xx.DNSNames)
if len(ds) == 0 {
continue
}
info := CertInfo{Domains: ds, NotBeforeTime: xx.NotBefore.Format("2006-01-02 15:04:05"), NotAfterTime: xx.NotAfter.Format("2006-01-02 15:04:05")}
res = append(res, info)
}
return &res, nil
}
func GetCertDomains(cert *tls.Certificate) []string {
var res []string
if cert == nil {
return res
}
for i := range cert.Certificate {
xx, err := x509.ParseCertificate(cert.Certificate[i])
if err != nil {
continue
}
for j := range xx.DNSNames {
d := strings.TrimSpace(xx.DNSNames[j])
if d == "" {
continue
}
res = append(res, d)
}
}
return res
}
// 除去空域名
func GetDomainsTrimSpace(dst []string) []string {
var res []string
for i := range dst {
if strings.TrimSpace(dst[i]) == "" {
continue
}
res = append(res, strings.TrimSpace(dst[i]))
}
return res
}
func GetDomainsStrByDomains(dst []string) string {
var res strings.Builder
for i := range dst {
d := strings.TrimSpace(dst[i])
if d == "" {
continue
}
if res.Len() > 0 {
res.WriteString(",")
}
res.WriteString(d)
}
return res.String()
}
//---------------------------------
func SSLCertficateListInit() {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var err error
for i := range programConfigure.SSLCertficateList {
err = programConfigure.SSLCertficateList[i].Init()
if err != nil {
log.Printf("SSLCertficateListInit [%s]err:%s", programConfigure.SSLCertficateList[i].Key, err.Error())
}
}
}
func GetSSLCertficateList() []SSLCertficate {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var res []SSLCertficate
if programConfigure == nil {
return res
}
for i := range programConfigure.SSLCertficateList {
res = append(res, programConfigure.SSLCertficateList[i])
}
return res
}
func SSLCertficateListAdd(s *SSLCertficate) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
//************
//重复检测
for i := range programConfigure.SSLCertficateList {
if programConfigure.SSLCertficateList[i].CertBase64 == s.CertBase64 {
return fmt.Errorf("绑定域名[%s]的相同证书已存在,请勿重复添加", (*s.CertsInfo)[0].Domains[0])
}
if programConfigure.SSLCertficateList[i].GetFirstDomain() != "" &&
programConfigure.SSLCertficateList[i].GetFirstDomain() == s.GetFirstDomain() {
return fmt.Errorf("绑定域名[%s]的证书已存在,如果要添加新证书请先手动删除旧证书", (*s.CertsInfo)[0].Domains[0])
}
}
//************
if s.Key == "" {
s.Key = stringsp.GetRandomString(8)
}
s.AddTime = time.Now().Format("2006-01-02 15:04:05")
s.Enable = true
programConfigure.SSLCertficateList = append(programConfigure.SSLCertficateList, *s)
return Save()
}
func SSLCertficateListDelete(key string) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
deleteIndex := -1
for i := range programConfigure.SSLCertficateList {
if programConfigure.SSLCertficateList[i].Key == key {
deleteIndex = i
break
}
}
if deleteIndex < 0 {
return fmt.Errorf("key:%s 不存在", key)
}
programConfigure.SSLCertficateList = DeleteSSLCertficateListslice(programConfigure.SSLCertficateList, deleteIndex)
return Save()
}
func SSLCertficateEnable(key string, enable bool) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.SSLCertficateList {
if programConfigure.SSLCertficateList[i].Key == key {
index = i
break
}
}
if index < 0 {
return fmt.Errorf("key:%s 不存在", key)
}
programConfigure.SSLCertficateList[index].Enable = enable
return Save()
}
func SSLCertficateAlterRemark(key, remark string) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.SSLCertficateList {
if programConfigure.SSLCertficateList[i].Key == key {
index = i
break
}
}
if index < 0 {
return fmt.Errorf("key:%s 不存在", key)
}
programConfigure.SSLCertficateList[index].Remark = remark
return Save()
}
func DeleteSSLCertficateListslice(a []SSLCertficate, deleteIndex int) []SSLCertficate {
j := 0
for i := range a {
if i != deleteIndex {
a[j] = a[i]
j++
}
}
return a[:j]
}
func GetValidSSLCertficateList() []tls.Certificate {
var res []tls.Certificate
var gdnRes []tls.Certificate
sslListCache := GetSSLCertficateList()
for _, s := range sslListCache {
if !s.Enable {
continue
}
if strings.HasPrefix(s.GetFirstDomain(), "*.") {
gdnRes = append(gdnRes, *s.Certificate)
continue
}
res = append(res, *s.Certificate)
}
res = append(res, gdnRes...)
return res
}

152
config/wol.go Normal file
View File

@ -0,0 +1,152 @@
package config
import (
"fmt"
"github.com/gdy666/lucky/thirdlib/gdylib/netinterfaces"
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
"github.com/gdy666/lucky/thirdlib/go-wol"
)
type WOLDevice struct {
Key string
DeviceName string
MacList []string
BroadcastIPs []string
Port int
Relay bool //交给中继设备发送
Repeat int //重复发送次数
}
func (d *WOLDevice) WakeUp() error {
return WakeOnLan(d.MacList, d.BroadcastIPs, d.Port, d.Repeat)
}
func WakeOnLan(macList []string, broadcastIps []string, port, repeat int) (err error) {
globalBroadcastList := netinterfaces.GetGlobalIPv4BroadcastList()
matchCount := 0
defer func() {
if matchCount <= 0 {
err = fmt.Errorf("找不到匹配的局域网广播IP,未能发送唤醒指令")
}
}()
if len(broadcastIps) > 0 {
for _, bcst := range broadcastIps {
bcstOk := stringsp.StrIsInList(bcst, globalBroadcastList)
if !bcstOk {
continue
}
matchCount++
for _, mac := range macList {
wol.WakeUpRepeat(mac, bcst, "", port, repeat)
}
}
return
}
for _, bcst := range globalBroadcastList {
matchCount++
for _, mac := range macList {
wol.WakeUpRepeat(mac, bcst, "", port, repeat)
}
}
return
}
//----------------------------------------
func GetWOLDeviceByKey(key string) *WOLDevice {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.WOLDeviceList {
if programConfigure.WOLDeviceList[i].Key == key {
index = i
break
}
}
if index < 0 {
return nil
}
device := programConfigure.WOLDeviceList[index]
return &device
}
func GetWOLDeviceList() []WOLDevice {
programConfigureMutex.RLock()
defer programConfigureMutex.RUnlock()
var res []WOLDevice
if programConfigure == nil {
return res
}
for i := range programConfigure.WOLDeviceList {
res = append(res, programConfigure.WOLDeviceList[i])
}
return res
}
func WOLDeviceListAdd(d *WOLDevice) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
if d.Key == "" {
d.Key = stringsp.GetRandomString(8)
}
programConfigure.WOLDeviceList = append(programConfigure.WOLDeviceList, *d)
return Save()
}
func WOLDeviceListAlter(d *WOLDevice) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
index := -1
for i := range programConfigure.WOLDeviceList {
if programConfigure.WOLDeviceList[i].Key == d.Key {
index = i
break
}
}
if index < 0 {
return fmt.Errorf("key:%s 不存在", d.Key)
}
programConfigure.WOLDeviceList[index] = *d
return Save()
}
func WOLDeviceListDelete(key string) error {
programConfigureMutex.Lock()
defer programConfigureMutex.Unlock()
deleteIndex := -1
for i := range programConfigure.WOLDeviceList {
if programConfigure.WOLDeviceList[i].Key == key {
deleteIndex = i
break
}
}
if deleteIndex < 0 {
return fmt.Errorf("key:%s 不存在", key)
}
programConfigure.WOLDeviceList = DeleteWOLDeviceListslice(programConfigure.WOLDeviceList, deleteIndex)
return Save()
}
func DeleteWOLDeviceListslice(a []WOLDevice, deleteIndex int) []WOLDevice {
j := 0
for i := range a {
if i != deleteIndex {
a[j] = a[i]
j++
}
}
return a[:j]
}

View File

@ -161,8 +161,9 @@ func getDomainItem(fullDomain string, domains *[]ddnscore.Domain) *ddnscore.Doma
func (d *DNSCommon) CreateHTTPClient() (*http.Client, error) {
ddnsGlobalConf := config.GetDDNSConfigure()
return httputils.CreateHttpClient(
"tcp",
d.task.DNS.GetCallAPINetwork(),
"",
!ddnsGlobalConf.HttpClientSecureVerify,
d.task.DNS.HttpClientProxyType,

View File

@ -2,7 +2,6 @@ package ddns
import (
"fmt"
"net/http"
"net/url"
"github.com/gdy666/lucky/ddnscore.go"
@ -142,12 +141,17 @@ func (dnspod *Dnspod) modify(result DnspodRecordListResp, domain *ddnscore.Domai
// 公共
func (dnspod *Dnspod) commonRequest(apiAddr string, values url.Values, domain *ddnscore.Domain) (status DnspodStatus, err error) {
resp, e := http.PostForm(
client, e := dnspod.CreateHTTPClient()
if e != nil {
err = e
return
}
resp, e := client.PostForm(
apiAddr,
values,
)
if err != nil {
if e != nil {
err = e
return
}

14
go.mod
View File

@ -3,15 +3,17 @@ module github.com/gdy666/lucky
go 1.18
require (
github.com/buger/jsonparser v1.1.1
github.com/eclipse/paho.mqtt.golang v1.4.1
github.com/fatedier/golib v0.2.0
github.com/gin-contrib/gzip v0.0.6
github.com/gin-gonic/gin v1.8.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/guonaihong/gout v0.3.1
github.com/miekg/dns v1.1.50
github.com/shirou/gopsutil/v3 v3.22.8
github.com/shirou/gopsutil/v3 v3.22.9
github.com/sirupsen/logrus v1.9.0
golang.org/x/net v0.0.0-20220921203646-d300de134e69
golang.org/x/net v0.0.0-20221004154528-8021a29435af
)
require (
@ -21,6 +23,7 @@ require (
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
@ -34,10 +37,11 @@ require (
github.com/tklauser/numcpus v0.5.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/tools v0.1.12 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

31
go.sum
View File

@ -1,7 +1,11 @@
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI=
github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
github.com/fatedier/golib v0.2.0 h1:8BxiUcjleBlXBYlTNUllD8KZZHaFU/NP/vP0Yu1Fkpg=
github.com/fatedier/golib v0.2.0/go.mod h1:e2NPpBGUFsHDjXrfP1B5aK3S0+yUeVxgqfc3go3KNj0=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
@ -30,11 +34,12 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/guonaihong/gout v0.3.1 h1:pj/44Jw0TTmcHF2RjMaCWhKPwCH98YuQejbN15Hts/o=
github.com/guonaihong/gout v0.3.1/go.mod h1:lhje0jRkh/gcIogrG22ENPITo9tylQa3kwD9eVxcDrk=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -76,8 +81,8 @@ github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:Om
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
github.com/shirou/gopsutil/v3 v3.22.9 h1:yibtJhIVEMcdw+tCTbOPiF1VcsuDeTE4utJ8Dm4c5eA=
github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -104,26 +109,29 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI=
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps=
golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4=
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -137,14 +145,15 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/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.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=

92
main.go
View File

@ -13,18 +13,11 @@ import (
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/ddns"
"github.com/gdy666/lucky/reverseproxy"
"github.com/gdy666/lucky/rule"
"github.com/gdy666/lucky/socketproxy"
)
var (
listenPort = flag.Int("p", 16601, "http Admin Web listen port ")
pcl = flag.Int64("pcl", -1, "global proxy count limit")
gpmc = flag.Int64("gpmc", -1, "global proxy max connections,default(1024)")
udpPackageSize = flag.Int("ups", socketproxy.UDP_DEFAULT_PACKAGE_SIZE, "udp package max size")
smc = flag.Int64("smc", socketproxy.TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS, "signle proxy max connections,default(128)")
upm = flag.Bool("upm", true, "udp proxy Performance Mode open")
udpshort = flag.Bool("udpshort", false, "udp short mode,eg dns")
configureFileURL = flag.String("c", "", "configure file url")
)
@ -50,7 +43,7 @@ func main() {
if err != nil {
log.Printf("%s", err.Error())
log.Printf("载入默认配置以及命令行设定的参数")
config.LoadDefault(*pcl, *listenPort, *gpmc)
config.LoadDefault(*listenPort)
if len(*configureFileURL) > 0 {
err = config.Save()
if err != nil {
@ -63,39 +56,31 @@ func main() {
config.BlackListInit()
config.WhiteListInit()
config.SSLCertficateListInit()
//fmt.Printf("*gcf:%v\n", *gcf)
socketproxy.SetSafeCheck(config.SafeCheck)
socketproxy.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections)
socketproxy.SetGlobalMaxProxyCount(gcf.BaseConfigure.ProxyCountLimit)
//socketproxy.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections)
//socketproxy.SetGlobalMaxProxyCount(gcf.BaseConfigure.ProxyCountLimit)
config.SetRunMode(runMode)
config.SetVersion(version)
log.Printf("RunMode:%s\n", runMode)
log.Printf("version:%s\tcommit %s, built at %s\n", version, commit, date)
RunAdminWeb(gcf.BaseConfigure.AdminWebListenPort, gcf.BaseConfigure.LogMaxSize)
RunAdminWeb(&gcf.BaseConfigure)
runTime = time.Now()
// if *upm {
// log.Printf("udp proxy Performance Mode open ")
// }
//LoadRuleFromConfigFile(gcf)
//log.Printf("Gobal proxy max connections:[%d] single proxy max connections:[%d]\n", base.GetGlobalMaxConnections(), base.GetSingleProxyMaxConnections(smc))
if len(flag.Args()) > 0 {
LoadRuleListFromCMD(flag.Args())
}
LoadRuleFromConfigFile(gcf)
rule.EnableAllRelayRule() //开启规则
config.PortForwardsRuleListInit()
//config.DDNSTaskListTaskDetailsInit()
config.DDNSTaskListConfigureCheck()
ddnsConf := config.GetDDNSConfigure()
if ddnsConf.Enable {
ddns.Run(time.Duration(ddnsConf.FirstCheckDelay)*time.Second, time.Duration(ddnsConf.Intervals)*time.Second)
go ddns.Run(time.Duration(ddnsConf.FirstCheckDelay)*time.Second, time.Duration(ddnsConf.Intervals)*time.Second)
}
reverseproxy.InitReverseProxyServer()
@ -123,43 +108,24 @@ func main() {
<-exit
}
func LoadRuleListFromCMD(args []string) {
options := socketproxy.RelayRuleOptions{UDPPackageSize: *udpPackageSize,
SingleProxyMaxConnections: *smc,
UDPProxyPerformanceMode: *upm,
UDPShortMode: *udpshort}
// func LoadRuleFromConfigFile(pc *config.ProgramConfigure) {
// if pc == nil {
// return
// }
// for i := range pc.RelayRuleList {
// relayRule, err := rule.CreateRuleByConfigureAndOptions(
// pc.RelayRuleList[i].Name,
// pc.RelayRuleList[i].Configurestr,
// pc.RelayRuleList[i].Options)
// if err != nil {
// continue
// }
// relayRule.From = "configureFile" //规则来源
// relayRule.IsEnable = pc.RelayRuleList[i].Enable
relayRules, err := rule.GetRelayRulesFromCMD(flag.Args(), &options)
if err != nil {
log.Print("config.GetRelayRulesFromCMD err:", err.Error())
return
}
_, e := rule.AddRuleToGlobalRuleList(false, (*relayRules)...)
if e != nil {
log.Printf("%s\n", e)
}
}
func LoadRuleFromConfigFile(pc *config.ProgramConfigure) {
if pc == nil {
return
}
for i := range pc.RelayRuleList {
relayRule, err := rule.CreateRuleByConfigureAndOptions(
pc.RelayRuleList[i].Name,
pc.RelayRuleList[i].Configurestr,
pc.RelayRuleList[i].Options)
if err != nil {
continue
}
relayRule.From = "configureFile" //规则来源
relayRule.IsEnable = pc.RelayRuleList[i].Enable
_, e := rule.AddRuleToGlobalRuleList(false, *relayRule)
if e != nil {
log.Printf("%s\n", e)
}
}
}
// _, e := rule.AddRuleToGlobalRuleList(false, *relayRule)
// if e != nil {
// log.Printf("%s\n", e)
// }
// }
// }

BIN
previews/wol001.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
previews/wol002.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -71,6 +71,6 @@ func GetAccessLogs(ruleKey, proxyKey string, pageSize, page int) (int, []any) {
if subRule == nil {
return 0, res
}
total, res = subRule.GetLogsBuffer().GetLogsByLimit(config.ReverseProxyLogConvert, pageSize, page)
total, res = subRule.GetLogsBuffer().GetLogsByLimit(config.WebLogConvert, pageSize, page)
return total, res
}

View File

@ -1,264 +0,0 @@
// Copyright 2022 gdy, 272288813@qq.com
package rule
import (
"fmt"
"sync"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/socketproxy"
)
var globalRelayRules *[]RelayRule
var globalRelayRulesMutex sync.RWMutex
func SetGlobalRelayRules(rl *[]RelayRule) {
globalRelayRulesMutex.Lock()
defer globalRelayRulesMutex.Unlock()
globalRelayRules = rl
syncRuleListToConfigure()
}
func AddRuleToGlobalRuleList(sync bool, rl ...RelayRule) (string, error) {
globalRelayRulesMutex.Lock()
defer globalRelayRulesMutex.Unlock()
var err error
if globalRelayRules == nil {
var rrl []RelayRule
globalRelayRules = &rrl
}
for i := range rl {
isExits := false
for j := range *globalRelayRules {
if (*globalRelayRules)[j].MainConfigure == rl[i].MainConfigure {
isExits = true
if err == nil {
err = fmt.Errorf("\n\t规则[%s]已存在,不再重复加入规则列表", rl[i].MainConfigure)
} else {
err = fmt.Errorf("%s\n\t规则[%s]已存在,不再重复加入规则列表", err.Error(), rl[i].MainConfigure)
}
break
}
}
if !isExits {
*globalRelayRules = append(*globalRelayRules, rl[i])
if rl[i].From != "cmd" && sync {
syncRes := ""
if syncErr := syncRuleListToConfigure(); syncErr != nil {
syncRes = syncErr.Error()
}
return syncRes, nil
}
}
}
return "", err
}
func AlterRuleInGlobalRuleListByKey(key string, rule *RelayRule) (bool, error) {
globalRelayRulesMutex.Lock()
defer globalRelayRulesMutex.Unlock()
keyIndex := -1
for i := range *globalRelayRules {
if (*globalRelayRules)[i].MainConfigure != key {
continue
}
keyIndex = i
break
}
if keyIndex < 0 {
return true, fmt.Errorf("修改转发规则失败,规则[%s]不存在", key)
}
(*globalRelayRules)[keyIndex].Disable()
(*globalRelayRules)[keyIndex] = *rule
syncErr := syncRuleListToConfigure()
syncSuccess := true
if syncErr != nil {
syncSuccess = false
}
return syncSuccess, nil
}
func DeleteGlobalRuleByKey(key string) (bool, error) {
globalRelayRulesMutex.Lock()
defer globalRelayRulesMutex.Unlock()
if globalRelayRules == nil {
return true, nil
}
deleteIndex := -1
for i := range *globalRelayRules {
if (*globalRelayRules)[i].MainConfigure == key {
deleteIndex = i
break
}
}
if deleteIndex < 0 {
return true, fmt.Errorf("relayRule[%s]no found,delete failed", key)
}
*globalRelayRules = DeleteRuleSlice(*globalRelayRules, deleteIndex)
syncError := syncRuleListToConfigure()
syncSuccess := true
if syncError != nil {
syncSuccess = false
}
return syncSuccess, nil
}
func GetRuleByMainConfigure(configStr string) *RelayRule {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
if globalRelayRules == nil {
return nil
}
for i := range *globalRelayRules {
//fmt.Printf("MainConfigure %s:::%s\n", (*globalRelayRules)[i].MainConfigure, configStr)
if (*globalRelayRules)[i].MainConfigure == configStr {
r := (*globalRelayRules)[i]
return &r
}
}
return nil
}
func GetGlobalEnableProxyCount() int64 {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
if globalRelayRules == nil {
return 0
}
count := int64(0)
for _, r := range *globalRelayRules {
if !r.IsEnable {
continue
}
if r.proxyList == nil {
continue
}
count += int64(len(*r.proxyList))
}
return count
}
// func StartAllProxy() {
// allProxyListMutex.Lock()
// defer allProxyListMutex.Unlock()
// if allProxyList == nil {
// return
// }
// for _, p := range *allProxyList {
// go p.StartProxy()
// }
// }
func EnableAllRelayRule() error {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
var err error
if globalRelayRules == nil {
return nil
}
for i := range *globalRelayRules {
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() <= socketproxy.GetGlobalMaxProxyCount() {
if (*globalRelayRules)[i].From == "cmd" || ((*globalRelayRules)[i].From == "configureFile" && (*globalRelayRules)[i].IsEnable) {
(*globalRelayRules)[i].Enable()
}
continue
}
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() > socketproxy.DEFAULT_MAX_PROXY_COUNT {
if err == nil {
err = fmt.Errorf("\n\t超出代理数最大限制,规则[%s]未启用", (*globalRelayRules)[i].MainConfigure)
} else {
err = fmt.Errorf("%s\n\t超出代理数最大限制,规则[%s]未启用", err.Error(), (*globalRelayRules)[i].MainConfigure)
}
}
}
return err
}
func EnableRelayRuleByKey(key string) (*RelayRule, bool, error) {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
for i := range *globalRelayRules {
if (*globalRelayRules)[i].MainConfigure == key {
(*globalRelayRules)[i].Enable()
syncErr := syncRuleListToConfigure()
synsSuccess := true
if syncErr != nil {
synsSuccess = false
}
return &(*globalRelayRules)[i], synsSuccess, nil
}
}
return nil, true, fmt.Errorf("规则[%s]不存在,开启失败", key)
}
func DisableRelayRuleByKey(key string) (*RelayRule, bool, error) {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
for i := range *globalRelayRules {
if (*globalRelayRules)[i].MainConfigure == key {
(*globalRelayRules)[i].Disable()
syncErr := syncRuleListToConfigure()
synsSuccess := true
if syncErr != nil {
synsSuccess = false
}
return &(*globalRelayRules)[i], synsSuccess, nil
}
}
return nil, true, fmt.Errorf("规则[%s]不存在,停用失败", key)
}
func DeleteRuleSlice(a []RelayRule, deleteIndex int) []RelayRule {
j := 0
for i := range a {
if i != deleteIndex {
a[j] = a[i]
j++
}
}
return a[:j]
}
// syncRuleListToConfigure 同步规则列表到配置
func syncRuleListToConfigure() error {
var ruleList []config.ConfigureRelayRule
for i := range *globalRelayRules {
if (*globalRelayRules)[i].From == "cmd" {
continue
}
rule := config.ConfigureRelayRule{
Name: (*globalRelayRules)[i].Name,
Configurestr: (*globalRelayRules)[i].MainConfigure,
Enable: (*globalRelayRules)[i].IsEnable,
Options: (*globalRelayRules)[i].Options}
ruleList = append(ruleList, rule)
}
config.SetConfigRuleList(&ruleList)
return config.Save()
}

View File

@ -1,66 +0,0 @@
// Copyright 2022 gdy, 272288813@qq.com
package rule
import "github.com/gdy666/lucky/socketproxy"
type RelayRuleProxyInfo struct {
Proxy string `json:"Proxy"`
CurrentConnections int64 `json:"CurrentConnections"`
TrafficIn int64 `json:"TrafficIn"`
TrafficOut int64 `json:"TrafficOut"`
}
func GetRelayRuleList() (*[]RelayRule, map[string][]RelayRuleProxyInfo) {
globalRelayRulesMutex.RLock()
defer globalRelayRulesMutex.RUnlock()
var rl []RelayRule
proxyInfoMap := make(map[string][]RelayRuleProxyInfo)
if globalRelayRules == nil {
return &rl, proxyInfoMap
}
for i := range *globalRelayRules {
rl = append(rl, (*globalRelayRules)[i])
var rpl []RelayRuleProxyInfo
if (*globalRelayRules)[i].proxyList == nil {
proxyInfoMap[(*globalRelayRules)[i].MainConfigure] = rpl
continue
}
for pindex := range *(*globalRelayRules)[i].proxyList {
pi := RelayRuleProxyInfo{
Proxy: (*(*globalRelayRules)[i].proxyList)[pindex].GetKey(),
CurrentConnections: (*(*globalRelayRules)[i].proxyList)[pindex].GetCurrentConnections(),
TrafficIn: (*(*globalRelayRules)[i].proxyList)[pindex].GetTrafficIn(),
TrafficOut: (*(*globalRelayRules)[i].proxyList)[pindex].GetTrafficOut()}
rpl = append(rpl, pi)
}
proxyInfoMap[(*globalRelayRules)[i].MainConfigure] = rpl
}
return &rl, proxyInfoMap
}
//GetAllProxyInfo
// func GetAllProxyInfo() map[string]interface{} {
// allProxyListMutex.Lock()
// defer allProxyListMutex.Unlock()
// info := make(map[string]interface{})
// if allProxyList == nil {
// return info
// }
// for _, p := range *allProxyList {
// pi := GetProxyInfo(p)
// info[p.GetKey()] = pi
// }
// return info
// }
func GetProxyInfo(p socketproxy.Proxy) map[string]string {
pi := make(map[string]string)
pi["proxyType"] = p.GetProxyType()
pi["key"] = p.GetKey()
pi["status"] = p.GetStatus()
pi["fromRule"] = p.FromRule()
return pi
}

View File

@ -1,546 +0,0 @@
// Copyright 2022 gdy, 272288813@qq.com
package rule
import (
"fmt"
"log"
"net"
"strconv"
"strings"
"github.com/gdy666/lucky/socketproxy"
)
type RelayRule struct {
Name string `json:"Name"`
MainConfigure string `json:"Mainconfigure"`
RelayType string `json:"RelayType"`
ListenIP string `json:"ListenIP"`
ListenPorts string `json:"ListenPorts"`
TargetIP string `json:"TargetIP"`
TargetPorts string `json:"TargetPorts"`
BalanceTargetAddressList []string `json:"BalanceTargetAddressList"`
Options socketproxy.RelayRuleOptions `json:"Options"`
SubRuleList []SubRelayRule `json:"SubRuleList"`
From string `json:"From"`
IsEnable bool `json:"Enable"`
proxyList *[]socketproxy.Proxy `json:"-"`
}
type SubRelayRule struct {
ProxyType string `json:"ProxyType"`
BindIP string `json:"BindIP"`
ListenPorts []int `json:"ListenPorts"`
TargetHost string `json:"TargetHost"`
TargetPorts []int `json:"TargetPorts"`
BalanceTargetAddressAddress []string `json:"BalanceTargetAddressAddress"`
}
func (r *RelayRule) Enable() {
r.IsEnable = true
if r.proxyList == nil {
return
}
for _, p := range *r.proxyList {
p.StartProxy()
}
}
func (r *RelayRule) GetProxyCount() int64 {
if r.proxyList == nil {
return 0
}
return int64(len(*r.proxyList))
}
func (r *RelayRule) Disable() {
r.IsEnable = false
if r.proxyList == nil {
return
}
for _, p := range *r.proxyList {
p.StopProxy()
}
}
func GetRelayRulesFromCMD(configureList []string, options *socketproxy.RelayRuleOptions) (relayRules *[]RelayRule, err error) {
//proxyMap := make(map[string]socketproxy.Proxy)
var relayRuleList []RelayRule
for _, configure := range configureList {
relayRule, err := CreateRuleByConfigureAndOptions("", configure, *options)
if err != nil {
return nil, err
}
relayRule.From = "cmd" //规则来源
relayRuleList = append(relayRuleList, *relayRule)
}
return &relayRuleList, nil
}
func (r *RelayRule) CreateMainConfigure() (configure string) {
if len(r.BalanceTargetAddressList) > 0 {
configure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, strings.Join(r.BalanceTargetAddressList, "|"))
} else {
if strings.Compare(r.ListenPorts, r.TargetPorts) == 0 {
configure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP)
} else {
configure = fmt.Sprintf("%s@%s:%sto%s:%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts)
}
}
return configure
}
func CreateRuleByConfigureAndOptions(name, configureStr string, options socketproxy.RelayRuleOptions) (rule *RelayRule, err error) {
var r RelayRule
r.Options = options
r.SubRuleList, r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts, r.BalanceTargetAddressList, err = createSubRuleListFromConfigure(configureStr)
if err != nil {
return nil, err
}
r.MainConfigure = r.CreateMainConfigure()
// if len(r.BalanceTargetAddressList) > 0 {
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, strings.Join(r.BalanceTargetAddressList, ","))
// } else {
// if strings.Compare(r.ListenPorts, r.TargetPorts) == 0 {
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP)
// } else {
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s:%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts)
// }
// }
var pl []socketproxy.Proxy
for i := range r.SubRuleList {
if len(r.BalanceTargetAddressList) == 0 {
for j := range r.SubRuleList[i].ListenPorts {
p, e := socketproxy.CreateProxy(r.SubRuleList[i].ProxyType,
r.SubRuleList[i].BindIP,
r.SubRuleList[i].TargetHost,
nil,
r.SubRuleList[i].ListenPorts[j],
r.SubRuleList[i].TargetPorts[j],
&options)
if e != nil {
log.Printf("CreateProxy error:%s", e.Error())
continue
}
p.SetFromRule(r.MainConfigure)
pl = append(pl, p)
}
continue
}
p, e := socketproxy.CreateProxy(r.SubRuleList[i].ProxyType,
r.SubRuleList[i].BindIP,
r.SubRuleList[i].TargetHost,
&r.BalanceTargetAddressList,
r.SubRuleList[i].ListenPorts[0],
0,
&options)
if e != nil {
log.Printf("CreateProxy error:%s", e.Error())
continue
}
p.SetFromRule(r.MainConfigure)
pl = append(pl, p)
}
r.proxyList = &pl
r.Name = name
return &r, nil
}
func createSubRuleListFromConfigure(str string) (subRelyList []SubRelayRule, proxytypeListStr, listenIP, listenPortsStr, targetIP, targetePortsStr string, targetAddressList []string, err error) {
//log.Printf("ConfigureStr:%s\n", str)
splitRes := strings.Split(str, "@")
if len(splitRes) > 2 {
err = fmt.Errorf("relay参数:%s格式有误!000", str)
return
}
proxytypeListStr = "tcp,udp"
relayConfig := splitRes[0]
if len(splitRes) == 2 {
proxytypeListStr = splitRes[0]
relayConfig = splitRes[1]
}
proxyTypeList := getProxyTypeList(proxytypeListStr)
err = checkProxyType(proxyTypeList)
if err != nil {
return
}
proxytypeListStr = convertProxyTypeByList(proxyTypeList)
relayConfigArray := strings.Split(relayConfig, "to")
if len(relayConfigArray) > 2 {
err = fmt.Errorf("relay参数:%s格式有误!001", str)
return
}
var listenPorts []int
var targetPorts []int
switch len(relayConfigArray) {
case 1: //监听端口没有指定 比如 192.168.31.22:80,443,20000-20010
tip, tPortsStr, e := getIpAndPortFromAddress(relayConfigArray[0], true, true)
if e != nil {
err = fmt.Errorf("参数中目标地址部分参数[%s]格式有误", relayConfigArray[0])
return
}
targetPorts, e = portsStrToIList(tPortsStr)
if e != nil {
err = fmt.Errorf("参数[%s]中的目标端口部分出错:%s", str, e.Error())
return
}
targetePortsStr = tPortsStr
listenPortsStr = tPortsStr
listenPorts = targetPorts
targetIP = tip
case 2: //监听端口有指定 比如 80,443,20000-20010to192.168.31.222 ,但目标地址的端口不一定指定
bindAddress := relayConfigArray[0]
bip, bPortsStr, e := getIpAndPortFromAddress(bindAddress, false, false)
if e != nil {
err = fmt.Errorf("参数[%s]中的监听端口部分出错:%s", str, e.Error())
return
}
listenIP = bip
listenPortsStr = bPortsStr
//fmt.Printf("bip:%s bindPortsStr:%s\n", bip, bindPortsStr)
listenPorts, e = portsStrToIList(bPortsStr)
if e != nil {
err = fmt.Errorf("参数中绑定端口部分参数[%s]格式有误", bPortsStr)
return
}
//log.Printf("relayConfigArray[1]:\t[%s]", relayConfigArray[1])
if strings.Contains(relayConfigArray[1], "|") { //均衡负载模式
if len(listenPorts) != 1 {
err = fmt.Errorf("均衡负载模式一条配置指定监听一个端口")
return
}
targetAddressList = strings.Split(relayConfigArray[1], "|")
} else {
targetAddress := relayConfigArray[1]
tip, tPortsStr, e := getIpAndPortFromAddress(targetAddress, true, false)
if e != nil {
err = fmt.Errorf("参数中目标地址部分参数[%s]格式有误", targetAddress)
return
}
targetePortsStr = tPortsStr
targetPorts, e = portsStrToIList(tPortsStr)
if e != nil {
err = fmt.Errorf("参数[%s]中的目标端口部分出错:%s", str, e.Error())
return
}
if len(listenPorts) > 0 && len(targetPorts) == 0 {
targetPorts = listenPorts
targetePortsStr = listenPortsStr
} else if len(listenPorts) == 0 && len(targetPorts) > 0 {
listenPorts = targetPorts
listenPortsStr = targetePortsStr
}
if len(listenPorts) != len(targetPorts) {
err = fmt.Errorf("参数[%s]中监听端口数量和目标端口数量不一致", str)
// fmt.Printf("listenPorts:%v\n", listenPorts)
// fmt.Printf("targetPorts:%v\n", targetPorts)
return
}
targetIP = tip
}
default:
}
var SubBaseRule SubRelayRule
SubBaseRule.BindIP = listenIP
SubBaseRule.ListenPorts = append(SubBaseRule.ListenPorts, listenPorts...)
if len(targetAddressList) == 0 {
SubBaseRule.TargetHost = targetIP
SubBaseRule.TargetPorts = append(SubBaseRule.TargetPorts, targetPorts...)
} else {
SubBaseRule.BalanceTargetAddressAddress = targetAddressList
}
for i := range proxyTypeList {
dt := SubBaseRule
dt.ProxyType = proxyTypeList[i]
subRelyList = append(subRelyList, dt)
}
return
}
func convertProxyTypeByList(proxyTypeList []string) (proxyType string) {
for i := range proxyTypeList {
if i == 0 {
proxyType = proxyTypeList[i]
continue
}
proxyType += "," + proxyTypeList[i]
}
return
}
func getProxyTypeList(proxyTypeListStr string) (proxyTypeList []string) {
//tmpList = strings.Split(proxyTypeListStr, ",")
//var
tmpMap := make(map[string]int)
if strings.Contains(proxyTypeListStr, "tcp4") && strings.Contains(proxyTypeListStr, "tcp6") {
proxyTypeList = append(proxyTypeList, "tcp")
tmpMap["tcp"] = 1
proxyTypeListStr = strings.Replace(proxyTypeListStr, "tcp4", ",", -1)
proxyTypeListStr = strings.Replace(proxyTypeListStr, "tcp6", ",", -1)
}
if strings.Contains(proxyTypeListStr, "udp4") && strings.Contains(proxyTypeListStr, "udp6") {
proxyTypeList = append(proxyTypeList, "udp")
tmpMap["udp"] = 1
proxyTypeListStr = strings.Replace(proxyTypeListStr, "udp4", ",", -1)
proxyTypeListStr = strings.Replace(proxyTypeListStr, "udp6", ",", -1)
}
tmpList := strings.Split(proxyTypeListStr, ",")
for i := range tmpList {
if len(tmpList[i]) <= 2 {
continue
}
if _, ok := tmpMap[tmpList[i]]; ok {
continue
}
_, tcpOK := tmpMap["tcp"]
_, udpOK := tmpMap["udp"]
if (tmpList[i] == "tcp4" || tmpList[i] == "tcp6") && tcpOK {
continue
}
if (tmpList[i] == "udp4" || tmpList[i] == "udp6") && udpOK {
continue
}
proxyTypeList = append(proxyTypeList, tmpList[i])
tmpMap[tmpList[i]] = 1
}
return
}
func checkProxyType(proxyTypeList []string) error {
for _, proxyType := range proxyTypeList {
switch proxyType {
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
{
return nil
}
default:
{
return fmt.Errorf("unsupport Proxy Type:%s", proxyType)
}
}
}
return nil
}
// CheckProxyConflict 冲突检查
func CheckProxyConflict(proxyList *[]socketproxy.Proxy, proxyType, listenIP string, listenPort int) error {
proxyMap := make(map[string]socketproxy.Proxy)
for i, p := range *proxyList {
proxyMap[p.GetKey()] = (*proxyList)[i]
}
key := socketproxy.GetProxyKey(proxyType, listenIP, listenPort)
if _, ok := proxyMap[key]; ok {
return fmt.Errorf("绑定的地址和端口存在冲突![%s]", key)
}
anyBindKey := fmt.Sprintf("%s@:%d", proxyType, listenPort)
if strings.Compare(key, anyBindKey) == 0 {
for exitsKey := range proxyMap {
if strings.HasSuffix(exitsKey, anyBindKey) {
return fmt.Errorf("绑定的地址和端口存在冲突![%s][%s]", key, exitsKey)
}
}
} else {
if _, ok := proxyMap[anyBindKey]; ok {
return fmt.Errorf("绑定的地址和端口存在冲突![%s]", key)
}
}
return nil
}
// getIpAndPortsFromAddress
func getIpAndPortFromAddress(address string, needip bool, needports bool) (ip string, ports string, err error) {
ipAndPortIndex := strings.LastIndex(address, ":")
// defer func() {
// fmt.Printf("\nFuck: [%s]--->[%s]", ip, ports)
// }()
if ipAndPortIndex < 0 || (!needip && !needports) {
switch {
case (!needip && needports): //地址中仅有端口
ports = address
case (needip && !needports): //地址中仅有ip
{
ip = address
}
case (!needip && !needports): //地址中 端口和ip都不是必须
{
if ipAndPortIndex > 0 {
ip = address[:ipAndPortIndex]
ports = address[ipAndPortIndex+1:]
break
}
//但address非空,判断
if address == "" {
break
}
if net.ParseIP(address) != nil {
ip = address
} else {
ports = address
if strings.HasPrefix(ports, ":") {
ports = ports[1:]
}
}
}
default:
}
return
}
ports = address[ipAndPortIndex+1:]
if ipAndPortIndex <= 1 {
//fmt.Printf("Fuck:%s\n", ports)
return
}
addressHost := address[:ipAndPortIndex]
addressHost = strings.Replace(addressHost, "[", "", -1)
addressHost = strings.Replace(addressHost, "]", "", -1)
if net.ParseIP(addressHost) == nil {
err = fmt.Errorf("ip[%s]格式有误", address[:ipAndPortIndex])
return
}
ip = addressHost
return
}
// portsStrToIList
func portsStrToIList(portsStr string) (ports []int, err error) {
if portsStr == "" {
return
}
if strings.Contains(portsStr, ",") {
tmpStrList := strings.Split(portsStr, ",")
for i := range tmpStrList {
tps, e := portsStrToIList(tmpStrList[i])
if e != nil {
err = fmt.Errorf("端口参数处理出错:%s", e.Error())
return
}
ports = append(ports, tps...)
}
return
}
portsStrList := strings.Split(portsStr, "-")
if len(portsStrList) > 2 {
err = fmt.Errorf("端口%s格式有误", portsStr)
return
}
if len(portsStrList) == 1 { //single listen port
listenPort, e := portStrToi(portsStrList[0])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", e.Error())
return
}
ports = append(ports, listenPort)
}
if len(portsStrList) == 2 {
minListenPort, e := portStrToi(portsStrList[0])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", portsStrList[0])
return
}
maxListenPort, e := portStrToi(portsStrList[1])
if e != nil {
err = fmt.Errorf("端口格式有误!%s", portsStrList[1])
return
}
if maxListenPort <= minListenPort {
err = fmt.Errorf("前一个端口[%d]要小于后一个端口[%d]", minListenPort, maxListenPort)
return
}
i := minListenPort
for {
if i > maxListenPort {
break
}
ports = append(ports, i)
i++
}
}
return
}
func portStrToi(portStr string) (int, error) {
port, err := strconv.Atoi(portStr)
if err != nil {
return 0, fmt.Errorf("端口格式有误:%s", err.Error())
}
if port < 1 || port > 65535 {
return 0, fmt.Errorf("端口[%d]超出范围", port)
}
return port, nil
}

View File

@ -1,251 +0,0 @@
package rule
import (
"testing"
)
func Test_createSubRuleListFromConfigure_1(t *testing.T) {
argsA := "53to192.168.31.1"
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 2 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[1].ProxyType != "udp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
if goportsList[1].ListenPorts[0] != 53 || goportsList[1].TargetPorts[0] != 53 {
t.Errorf("createSubRuleListFromConfigure [%s] testE error %v", argsA, goportsList)
return
}
}
func Test_createSubRuleListFromConfigure_2(t *testing.T) {
argsA := "tcp@53to192.168.31.1"
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
}
func Test_createSubRuleListFromConfigure_3(t *testing.T) {
argsA := "udp4@53to192.168.31.1"
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "udp4" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
}
func Test_createSubRuleListFromConfigure_4_0(t *testing.T) {
argsList := []string{"tcp@20000-20021to192.168.31.1", "tcp@192.168.31.1:20000-20021", "tcp@20000-20021to192.168.31.1:20000-20021"}
for _, argsA := range argsList {
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 22 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 22 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 20000 || goportsList[0].TargetPorts[0] != 20000 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[21] != 20021 || goportsList[0].TargetPorts[21] != 20021 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
}
func Test_createSubRuleListFromConfigure_4_1(t *testing.T) {
argsA := "tcp@30000-30021to192.168.31.1:20000-20021"
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 22 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 22 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 30000 || goportsList[0].TargetPorts[0] != 20000 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[21] != 30021 || goportsList[0].TargetPorts[21] != 20021 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
func Test_createSubRuleListFromConfigure_5_0(t *testing.T) {
//argsA :=
args := []string{"tcp@80,443,20000-20021to192.168.31.1:80,443,20000-20021",
"tcp@192.168.31.1:80,443,20000-20021",
"tcp@80,443,20000-20021to192.168.31.1"}
for _, argsA := range args {
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 24 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 24 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 80 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[23] != 20021 || goportsList[0].TargetPorts[23] != 20021 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
}
func Test_createSubRuleListFromConfigure_5_1(t *testing.T) {
//argsA :=
args := []string{"tcp@80,443,30000-30021to192.168.31.1:81,443,20000-20021"}
for _, argsA := range args {
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 24 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 24 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 81 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[23] != 30021 || goportsList[0].TargetPorts[23] != 20021 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
}
func Test_createSubRuleListFromConfigure_6_0(t *testing.T) {
//argsA :=
args := []string{"tcp6@80,443to192.168.31.1:80,443",
"tcp6@80,443to192.168.31.1",
"tcp6@192.168.31.1:80,443"}
for _, argsA := range args {
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp6" || len(goportsList[0].ListenPorts) != 2 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 2 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 80 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[1] != 443 || goportsList[0].TargetPorts[1] != 443 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
}
func Test_createSubRuleListFromConfigure_6_1(t *testing.T) {
//argsA :=
args := []string{"tcp@80,443to192.168.31.1:81,443"}
for _, argsA := range args {
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
if e != nil || len(goportsList) != 1 {
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
return
}
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 2 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 2 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 81 {
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
return
}
if goportsList[0].ListenPorts[1] != 443 || goportsList[0].TargetPorts[1] != 443 {
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
return
}
}
}

View File

@ -11,8 +11,6 @@ type BaseProxyConf struct {
key string
ProxyType string // tcp tcp4 tcp6 udp udp4 udp6
//TrafficMonitor bool //流量监控
fromRule string
}
func (p *BaseProxyConf) GetProxyType() string {
@ -23,14 +21,6 @@ func (p *BaseProxyConf) GetStatus() string {
return p.ProxyType
}
func (p *BaseProxyConf) SetFromRule(rule string) {
p.fromRule = rule
}
func (p *BaseProxyConf) FromRule() string {
return p.fromRule
}
func (p *BaseProxyConf) ReceiveDataCallback(nw int64) {
atomic.AddInt64(&p.TrafficIn, nw)
}

View File

@ -9,6 +9,7 @@ import (
"sync"
"github.com/gdy666/lucky/thirdlib/gdylib/pool"
"github.com/sirupsen/logrus"
)
type Proxy interface {
@ -23,8 +24,7 @@ type Proxy interface {
GetListenPort() int
GetKey() string
GetCurrentConnections() int64
SetFromRule(string)
FromRule() string
String() string
GetTrafficIn() int64
GetTrafficOut() int64
@ -32,11 +32,12 @@ type Proxy interface {
}
type RelayRuleOptions struct {
UDPPackageSize int `json:"UDPPackageSize,omitempty"`
SingleProxyMaxConnections int64 `json:"SingleProxyMaxConnections,omitempty"`
UDPProxyPerformanceMode bool `json:"UDPProxyPerformanceMode,omitempty"`
UDPShortMode bool `json:"UDPShortMode,omitempty"`
SafeMode string `json:"SafeMode,omitempty"`
UDPPackageSize int `json:"UDPPackageSize,omitempty"`
SingleProxyMaxTCPConnections int64 `json:"SingleProxyMaxTCPConnections,omitempty"`
SingleProxyMaxUDPReadTargetDatagoroutineCount int64 `json:"SingleProxyMaxUDPReadTargetDatagoroutineCount"`
UDPProxyPerformanceMode bool `json:"UDPProxyPerformanceMode,omitempty"`
UDPShortMode bool `json:"UDPShortMode,omitempty"`
SafeMode string `json:"SafeMode,omitempty"`
}
// Join two io.ReadWriteCloser and do some operations.
@ -157,16 +158,16 @@ func formatFileSize(fileSize int64) (size string) {
}
func CreateProxy(proxyType, listenIP, targetHost string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) (p Proxy, err error) {
func CreateProxy(log *logrus.Logger, proxyType, listenIP string, targetAddressList []string, listenPort, targetPort int, options *RelayRuleOptions) (p Proxy, err error) {
//key := GetProxyKey(proxyType, listenIP, listenPort)
switch {
case strings.HasPrefix(proxyType, "tcp"):
{
return CreateTCPProxy(proxyType, listenIP, targetHost, balanceTargetAddressList, listenPort, targetPort, options), nil
return CreateTCPProxy(log, proxyType, listenIP, targetAddressList, listenPort, targetPort, options), nil
}
case strings.HasPrefix(proxyType, "udp"):
{
return CreateUDPProxy(proxyType, listenIP, targetHost, balanceTargetAddressList, listenPort, targetPort, options), nil
return CreateUDPProxy(log, proxyType, listenIP, targetAddressList, listenPort, targetPort, options), nil
}
default:
return nil, fmt.Errorf("未支持的类型:%s", proxyType)

View File

@ -7,6 +7,8 @@ import (
"net"
"strings"
"sync"
"github.com/sirupsen/logrus"
)
type TCPProxy struct {
@ -20,20 +22,18 @@ type TCPProxy struct {
connMapMutex sync.Mutex
}
func CreateTCPProxy(proxyType, listenIP, targetIP string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) *TCPProxy {
func CreateTCPProxy(log *logrus.Logger, proxyType, listenIP string, targetAddressList []string, listenPort, targetPort int, options *RelayRuleOptions) *TCPProxy {
p := &TCPProxy{}
p.ProxyType = proxyType
p.listenIP = listenIP
p.listenPort = listenPort
p.targetIP = targetIP
p.targetAddressList = targetAddressList
p.targetPort = targetPort
if balanceTargetAddressList != nil {
p.balanceTargetAddressList = *balanceTargetAddressList
}
p.log = log
p.safeMode = options.SafeMode
p.SetMaxConnections(options.SingleProxyMaxConnections)
p.SetMaxConnections(options.SingleProxyMaxTCPConnections)
return p
}
@ -41,24 +41,26 @@ func (p *TCPProxy) GetStatus() string {
return fmt.Sprintf("%s\nactivity connections:[%d]", p.String(), p.GetCurrentConnections())
}
// func (p *TCPProxy) CheckConns() bool {
// if GetGlobalTCPConns() >= tcpGlobalMaxConnections || p.GetCurrentConnections() >= p.TcpSingleProxyMaxConns {
// // if p.GetGlobalTCPConns() >= tcpGlobalMaxConnections {
// // log.Println("")
// // }
// // if p.GetCurrentTCPConns() >= p.TcpSingleProxyMaxConns {
// // log.Printf("超出单代理TCP限制")
// // }
// return false
// }
// return true
// }
func (p *TCPProxy) CheckConnectionsLimit() error {
if GetGlobalTCPPortForwardConnections() >= GetGlobalTCPPortforwardMaxConnections() {
return fmt.Errorf("超出TCP最大总连接数[%d]限制", GetGlobalTCPPortforwardMaxConnections())
}
if p.GetCurrentConnections() >= p.SingleProxyMaxConnections {
return fmt.Errorf("超出单端口TCP最大连接数[%d]限制", p.SingleProxyMaxConnections)
}
//全局,单端口限制
return nil
}
func (p *TCPProxy) StartProxy() {
p.listenConnMutex.Lock()
defer p.listenConnMutex.Unlock()
if p.listenConn != nil {
log.Printf("proxy %s is started", p.String())
//log.Printf("proxy %s is started", p.String())
p.log.Warnf("proxy %s is started", p.String())
return
}
@ -69,16 +71,16 @@ func (p *TCPProxy) StartProxy() {
if err != nil {
if strings.Contains(err.Error(), "Only one usage of each socket address") {
log.Printf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
p.log.Errorf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
} else {
log.Printf("Cannot start proxy[%s]:%s", p.String(), err)
p.log.Errorf("Cannot start proxy[%s]:%s", p.String(), err)
}
return
}
p.listenConn = ln
log.Printf("[proxy][start][%s]", p.String())
p.log.Infof("[端口转发][开启][%s]", p.String())
go func() {
for {
@ -88,36 +90,32 @@ func (p *TCPProxy) StartProxy() {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
log.Printf(" Cannot accept connection due to error %s", err.Error())
p.log.Errorf(" Cannot accept connection due to error %s", err.Error())
continue
}
err = p.CheckConnectionsLimit()
if err != nil {
//p.PrintConnectionsInfo()
p.log.Warnf("[%s]超出最大连接数限制,不再接受新连接:%s", p.GetKey(), err.Error())
newConn.Close()
continue
}
newConnAddr := newConn.RemoteAddr().String()
if !p.SafeCheck(newConnAddr) {
log.Printf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr)
p.log.Warnf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr)
newConn.Close()
continue
}
//log.Printf("[%s]新连接[%s]安全检查通过", p.GetKey(), newConnAddr)
//fmt.Printf("连接IP:[%s]\n", newConn.RemoteAddr().String())
//log.Printf("new tdp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, newConn.RemoteAddr().String(), p.TargetAddress)
if !p.CheckConnections() {
//log.Printf("超出最大连接数限制\n")
p.PrintConnectionsInfo()
log.Printf("[%s]超出最大连接数限制,不再接受新连接", p.GetKey())
newConn.Close()
continue
}
p.log.Infof("[%s]新连接[%s]安全检查通过", p.GetKey(), newConnAddr)
p.connMapMutex.Lock()
p.connMap[newConn.RemoteAddr().String()] = newConn
p.connMapMutex.Unlock()
//atomic.AddInt64(&tcpconnections, 1)
p.AddCurrentConnections(1)
//fmt.Printf("当前全局TCP连接数:%d\n", p.GetGlobalTCPConns())
go p.handle(newConn)
}
}()
@ -134,7 +132,7 @@ func (p *TCPProxy) StopProxy() {
p.listenConnMutex.Lock()
defer p.listenConnMutex.Unlock()
defer func() {
log.Printf("[proxy][stop][%s]", p.String())
p.log.Infof("[端口转发][关闭][%s]", p.String())
}()
if p.listenConn == nil {
return
@ -152,7 +150,6 @@ func (p *TCPProxy) StopProxy() {
}
func (p *TCPProxy) handle(conn net.Conn) {
//dialer := net.Dialer{Timeout: 10 * time.Second}
//targetConn, err := dialer.Dial("tcp", p.TargetAddress)
targetConn, err := net.Dial("tcp", p.GetTargetAddress())
@ -166,7 +163,9 @@ func (p *TCPProxy) handle(conn net.Conn) {
p.connMapMutex.Lock()
delete(p.connMap, conn.RemoteAddr().String())
p.log.Infof("[%s]%s 断开连接", p.GetKey(), conn.RemoteAddr().String())
p.connMapMutex.Unlock()
}()
if err != nil {

View File

@ -3,23 +3,28 @@ package socketproxy
import (
"fmt"
"log"
"net"
"strings"
"sync"
"sync/atomic"
"github.com/sirupsen/logrus"
)
const TCP_DEFAULT_STREAM_BUFFERSIZE = 128
const DEFAULT_GLOBAL_MAX_CONNECTIONS = int64(10240)
const DEFAULT_GLOBAL_MAX_CONNECTIONS = int64(1024)
const DEFAULT_GLOBAL_UDPReadTargetDataMaxgoroutineCount = int64(1024)
const TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS = int64(256)
const DEFAULT_MAX_PROXY_COUNT = int64(128)
var globalMaxConnections = DEFAULT_GLOBAL_MAX_CONNECTIONS
const DEFAULT_MAX_PORTFORWARDS_LIMIT = int64(128)
var globalCurrentConnections int64 = 0
var gloMaxProxyCount int64 = DEFAULT_MAX_PROXY_COUNT
var globalTCPPortforwardMaxConnectionsLimit = DEFAULT_GLOBAL_MAX_CONNECTIONS
var globalUDPReadTargetDataMaxgoroutineCountLimit = DEFAULT_GLOBAL_UDPReadTargetDataMaxgoroutineCount
var globalTCPPortForwardCurrentConnections int64 = 0
var globalUDPPortForwardCurrentGroutineCount int64 = 0
var gloMaxPortForwardsCountLimit int64 = DEFAULT_MAX_PORTFORWARDS_LIMIT
var safeCheckFunc func(mode, ip string) bool
@ -27,66 +32,68 @@ func SetSafeCheck(f func(mode, ip string) bool) {
safeCheckFunc = f
}
func SetGlobalMaxProxyCount(max int64) {
atomic.StoreInt64(&gloMaxProxyCount, max)
func SetGlobalUDPReadTargetDataMaxgoroutineCountLimit(max int64) {
atomic.StoreInt64(&globalUDPReadTargetDataMaxgoroutineCountLimit, max)
}
func GetGlobalMaxProxyCount() int64 {
return atomic.LoadInt64(&gloMaxProxyCount)
func GetGlobalUDPReadTargetDataMaxgoroutineCountLimit() int64 {
return atomic.LoadInt64(&globalUDPReadTargetDataMaxgoroutineCountLimit)
}
func SetGlobalMaxConnections(max int64) {
atomic.StoreInt64(&globalMaxConnections, max)
func SetGlobalMaxPortForwardsCountLimit(max int64) {
atomic.StoreInt64(&gloMaxPortForwardsCountLimit, max)
}
func GetGlobalMaxConnections() int64 {
return atomic.LoadInt64(&globalMaxConnections)
func GetGlobalMaxPortForwardsCountLimit() int64 {
return atomic.LoadInt64(&gloMaxPortForwardsCountLimit)
}
func GetSingleProxyMaxConnections(m *int64) int64 {
if *m <= 0 {
return TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS
}
return *m
func SetGlobalTCPPortforwardMaxConnections(max int64) {
atomic.StoreInt64(&globalTCPPortforwardMaxConnectionsLimit, max)
}
func GetGlobalConnections() int64 {
return atomic.LoadInt64(&globalCurrentConnections)
func GetGlobalTCPPortforwardMaxConnections() int64 {
return atomic.LoadInt64(&globalTCPPortforwardMaxConnectionsLimit)
}
func GloBalCOnnectionsAdd(add int64) int64 {
return atomic.AddInt64(&globalCurrentConnections, add)
func GetGlobalTCPPortForwardConnections() int64 {
return atomic.LoadInt64(&globalTCPPortForwardCurrentConnections)
}
func GloBalTCPPortForwardConnectionsAdd(add int64) int64 {
return atomic.AddInt64(&globalTCPPortForwardCurrentConnections, add)
}
func GetGlobalUDPPortForwardGroutineCount() int64 {
return atomic.LoadInt64(&globalUDPPortForwardCurrentGroutineCount)
}
func GloBalUDPPortForwardGroutineCountAdd(add int64) int64 {
return atomic.AddInt64(&globalUDPPortForwardCurrentGroutineCount, add)
}
type TCPUDPProxyCommonConf struct {
CurrentConnectionsCount int64
SingleProxyMaxConnections int64
targetBalanceIndex int64
BaseProxyConf
listentAddress string
listenIP string
listenPort int
targetIP string
targetPort int
targetAddress string
balanceTargetAddressList []string //均衡负载转发
targetBalanceIndexMutex sync.Mutex
//targetIP string
targetAddressList []string
targetAddressCount int
targetAddressIndex uint64
targetAddressLock sync.Mutex
targetPort int
safeMode string
log *logrus.Logger
}
func (p *TCPUDPProxyCommonConf) CheckConnections() bool {
if p.GetCurrentConnections() >= GetGlobalMaxConnections() || p.GetCurrentConnections() >= p.SingleProxyMaxConnections {
return false
}
return true
}
func (p *TCPUDPProxyCommonConf) PrintConnectionsInfo() {
log.Printf("[%s]当前连接数:[%d],单代理最大连接数限制[%d],全局最大连接数限制[%d]\n", p.GetKey(), p.GetCurrentConnections(), p.SingleProxyMaxConnections, GetGlobalMaxConnections())
}
// func (p *TCPUDPProxyCommonConf) PrintConnectionsInfo() {
// p.log.Infof("[%s]当前连接数:[%d],当前端口最大TCP连接数限制[%d],全局最大TCP连接数限制[%d]", p.GetKey(), p.GetCurrentConnections(), p.SingleProxyMaxConnections, GetGlobalTCPPortforwardMaxConnections())
// }
func (p *TCPUDPProxyCommonConf) SetMaxConnections(max int64) {
if max <= 0 {
@ -98,17 +105,22 @@ func (p *TCPUDPProxyCommonConf) SetMaxConnections(max int64) {
func (p *TCPUDPProxyCommonConf) AddCurrentConnections(a int64) {
atomic.AddInt64(&p.CurrentConnectionsCount, a)
GloBalCOnnectionsAdd(a)
if strings.HasPrefix(p.ProxyType, "tcp") {
GloBalTCPPortForwardConnectionsAdd(a)
return
}
if strings.HasPrefix(p.ProxyType, "udp") {
GloBalUDPPortForwardGroutineCountAdd(a)
return
}
}
func (p *TCPUDPProxyCommonConf) GetCurrentConnections() int64 {
return atomic.LoadInt64(&p.CurrentConnectionsCount)
}
func (p *TCPProxy) GetCurrentCon() int64 {
return atomic.LoadInt64(&p.CurrentConnectionsCount)
}
func (p *TCPUDPProxyCommonConf) GetListentAddress() string {
if p.listentAddress == "" {
if strings.Contains(p.listenIP, ":") {
@ -136,33 +148,19 @@ func (p *TCPUDPProxyCommonConf) GetListenPort() int {
}
func (p *TCPUDPProxyCommonConf) GetTargetAddress() string {
if len(p.balanceTargetAddressList) == 0 {
if p.targetAddress == "" {
if strings.Contains(p.targetIP, ":") {
p.targetAddress = fmt.Sprintf("[%s]:%d", p.targetIP, p.targetPort)
} else {
p.targetAddress = fmt.Sprintf("%s:%d", p.targetIP, p.targetPort)
}
}
return p.targetAddress
p.targetAddressLock.Lock()
defer p.targetAddressLock.Unlock()
if p.targetAddressCount <= 0 {
p.targetAddressCount = len(p.targetAddressList)
p.targetAddressIndex = 0
}
var address string
addressListLength := int64(len(p.balanceTargetAddressList))
p.targetBalanceIndexMutex.Lock()
address = p.balanceTargetAddressList[p.targetBalanceIndex%addressListLength]
p.targetBalanceIndex++
p.targetBalanceIndexMutex.Unlock()
address := fmt.Sprintf("%s:%d", p.targetAddressList[p.targetAddressIndex%uint64(p.targetAddressCount)], p.targetPort)
p.targetAddressIndex++
return address
}
func (p *TCPUDPProxyCommonConf) String() string {
if len(p.balanceTargetAddressList) == 0 {
return fmt.Sprintf("%s@%s ===> %s", p.ProxyType, p.GetListentAddress(), p.GetTargetAddress())
}
return fmt.Sprintf("%s@%s ===> %v", p.ProxyType, p.GetListentAddress(), p.balanceTargetAddressList)
return fmt.Sprintf("%s@%v ===> %v:%d", p.ProxyType, p.GetListentAddress(), p.targetAddressList, p.targetPort)
}
func (p *TCPUDPProxyCommonConf) SafeCheck(remodeAddr string) bool {

View File

@ -3,7 +3,6 @@ package socketproxy
import (
"fmt"
"log"
"net"
"runtime"
"strings"
@ -12,6 +11,7 @@ import (
"github.com/fatedier/golib/errors"
"github.com/gdy666/lucky/thirdlib/gdylib/pool"
"github.com/sirupsen/logrus"
)
const UDP_DEFAULT_PACKAGE_SIZE = 1500
@ -29,11 +29,14 @@ type UDPProxy struct {
relayChs []chan *udpPackge
replyCh chan *udpPackge
udpPackageSize int
targetudpConnItemMap map[string]*udpMapItem
targetudpConnItemMapMutex sync.RWMutex
Upm bool //性能模式
ShortMode bool
udpPackageSize int
//targetudpConnItemMap map[string]*udpMapItem
//targetudpConnItemMapMutex sync.RWMutex
targetConnectSessions sync.Map
Upm bool //性能模式
ShortMode bool
isStop bool
SingleProxyMaxUDPReadTargetDatagoroutineCount int64
}
type udpPackge struct {
@ -42,21 +45,25 @@ type udpPackge struct {
remoteAddr *net.UDPAddr
}
func CreateUDPProxy(proxyType, listenIP, targetIP string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) *UDPProxy {
type udpTagetConSession struct {
targetConn *net.UDPConn
lastTime time.Time
}
func CreateUDPProxy(log *logrus.Logger, proxyType, listenIP string, targetAddressList []string, listenPort, targetPort int, options *RelayRuleOptions) *UDPProxy {
p := &UDPProxy{}
//p.Key = key
p.ProxyType = proxyType
p.listenIP = listenIP
p.listenPort = listenPort
p.targetIP = targetIP
p.targetAddressList = targetAddressList
p.targetPort = targetPort
if balanceTargetAddressList != nil {
p.balanceTargetAddressList = *balanceTargetAddressList
}
p.Upm = options.UDPProxyPerformanceMode
p.ShortMode = options.UDPShortMode
p.safeMode = options.SafeMode
p.log = log
p.SingleProxyMaxUDPReadTargetDatagoroutineCount = options.SingleProxyMaxUDPReadTargetDatagoroutineCount
p.SetUDPPacketSize(options.UDPPackageSize)
return p
@ -97,16 +104,16 @@ func (p *UDPProxy) StartProxy() {
bindAddr, err := net.ResolveUDPAddr(p.ProxyType, p.GetListentAddress())
if err != nil {
log.Printf("Cannot start proxy[%s]:%s", p.GetKey(), err)
p.log.Errorf("Cannot start proxy[%s]:%s", p.GetKey(), err)
return
}
ln, err := net.ListenUDP(p.ProxyType, bindAddr)
if err != nil {
if strings.Contains(err.Error(), " bind: Only one usage of each socket address") {
log.Printf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
p.log.Errorf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
} else {
log.Printf("Cannot start proxy[%s]:%s", p.String(), err)
p.log.Errorf("Cannot start proxy[%s]:%s", p.String(), err)
}
return
}
@ -116,17 +123,8 @@ func (p *UDPProxy) StartProxy() {
p.listenConn = ln
log.Printf("[proxy][start][%s]", p.String())
p.log.Infof("[端口转发][开启][%s]", p.String())
// p.targetAddr, err = net.ResolveUDPAddr(p.ProxyType, p.TargetAddress)
// if err != nil {
// log.Printf("net.ResolveUDPAddr[%s] error:%s", p.TargetAddress, err.Error())
// return
// }
//go p.test()
//p.relayCh = make(chan *udpPackge, 1024)
p.relayChs = make([]chan *udpPackge, p.getHandlegoroutineNum())
for i := range p.relayChs {
@ -134,19 +132,20 @@ func (p *UDPProxy) StartProxy() {
}
p.replyCh = make(chan *udpPackge, 1024)
if p.targetudpConnItemMap == nil {
p.targetudpConnItemMap = make(map[string]*udpMapItem)
}
// if p.targetudpConnItemMap == nil {
// p.targetudpConnItemMap = make(map[string]*udpMapItem)
// }
for i := range p.relayChs {
go p.Forwarder(i, p.relayChs[i])
}
go p.replyDataToRemotAddress()
go p.CheckTargetUDPConn()
go p.CheckTargetUDPConnectSessions()
for i := 0; i < p.getHandlegoroutineNum(); i++ {
go p.ListenFunc(ln)
go p.ListenHandler(ln)
}
}
@ -155,14 +154,13 @@ func (p *UDPProxy) StopProxy() {
p.listenConnMutex.Lock()
defer p.listenConnMutex.Unlock()
defer func() {
p.targetudpConnItemMapMutex.Lock()
for _, v := range p.targetudpConnItemMap {
v.conn.Close()
}
p.targetudpConnItemMap = nil
p.targetudpConnItemMap = make(map[string]*udpMapItem)
p.targetudpConnItemMapMutex.Unlock()
log.Printf("[proxy][stop][%s]", p.String())
p.targetConnectSessions.Range(func(key any, value any) bool {
session := value.(*udpTagetConSession)
session.targetConn.Close()
p.targetConnectSessions.Delete(key)
return true
})
p.log.Infof("[端口转发][关闭][%s]", p.String())
}()
if p.listenConn == nil {
@ -170,7 +168,11 @@ func (p *UDPProxy) StopProxy() {
}
p.listenConn.Close()
p.listenConn = nil
p.isStop = true
close(p.replyCh)
for i := range p.relayChs {
close(p.relayChs[i])
}
}
// ReadFromTargetOnce one clientAddr only read once,short mode eg: udp dns
@ -181,11 +183,11 @@ func (p *UDPProxy) ReadFromTargetOnce() bool {
return false
}
func (p *UDPProxy) GetStatus() string {
return fmt.Sprintf("%s max packet size[%d]", p.String(), p.GetUDPPacketSize())
}
// func (p *UDPProxy) GetStatus() string {
// return fmt.Sprintf("%s max packet size[%d]", p.String(), p.GetUDPPacketSize())
// }
func (p *UDPProxy) ListenFunc(ln *net.UDPConn) {
func (p *UDPProxy) ListenHandler(ln *net.UDPConn) {
inDatabuf := pool.GetBuf(p.GetUDPPacketSize())
defer pool.PutBuf(inDatabuf)
@ -195,40 +197,33 @@ func (p *UDPProxy) ListenFunc(ln *net.UDPConn) {
break
}
inDatabufSize, clientAddr, err := ln.ReadFromUDP(inDatabuf)
inDatabufSize, remoteAddr, err := ln.ReadFromUDP(inDatabuf)
if err != nil {
if strings.Contains(err.Error(), `smaller than the datagram`) {
log.Printf("%s ReadFromUDP error,the udp packet size is smaller than the datagram,please use flag '-ups xxx'set udp packet size \n", p.String())
p.log.Errorf("[%s] UDP包最大长度设置过小,请重新设置", p.GetKey())
} else {
if !strings.Contains(err.Error(), "use of closed network connection") {
log.Printf(" %s ReadFromUDP error:\n%s \n", p.String(), err.Error())
p.log.Errorf(" %s ReadFromUDP error:\n%s \n", p.String(), err.Error())
}
}
continue
}
//fmt.Printf("inDatabufSize:%d\n", inDatabufSize)
newConnAddr := clientAddr.String()
if !p.SafeCheck(newConnAddr) {
log.Printf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr)
remoteAddrStr := remoteAddr.String()
if !p.SafeCheck(remoteAddrStr) {
p.log.Warnf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), remoteAddrStr)
continue
}
// var newConOk bool
// p.targetudpConnItemMapMutex.RLock()
// _, newConOk = p.targetudpConnItemMap[clientAddr.String()]
// p.targetudpConnItemMapMutex.RUnlock()
// if !newConOk {
// log.Printf("new udp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, clientAddr.String(), p.TargetAddress)
// }
//log.Printf("new udp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, clientAddr.String(), p.TargetAddress)
_, ok := p.targetConnectSessions.Load(remoteAddrStr)
if !ok {
p.log.Infof("[%s]新连接 [%s]安全检查通过", p.GetKey(), remoteAddrStr)
}
data := pool.GetBuf(inDatabufSize)
copy(data, inDatabuf[:inDatabufSize])
inUdpPack := udpPackge{dataSize: inDatabufSize, data: &data, remoteAddr: clientAddr}
//p.relayCh <- &inUdpPack
inUdpPack := udpPackge{dataSize: inDatabufSize, data: &data, remoteAddr: remoteAddr}
p.relayChs[i%uint64(p.getHandlegoroutineNum())] <- &inUdpPack
i++
@ -236,150 +231,146 @@ func (p *UDPProxy) ListenFunc(ln *net.UDPConn) {
}
}
type udpMapItem struct {
conn *net.UDPConn
lastTime time.Time
func (p *UDPProxy) handlerDataFromTargetAddress(raddr *net.UDPAddr, tgConn *net.UDPConn) {
readBuffer := pool.GetBuf(p.GetUDPPacketSize())
var session *udpTagetConSession
sessionKey := raddr.String()
defer func() {
pool.PutBuf(readBuffer)
if p.ReadFromTargetOnce() {
tgConn.Close()
} else {
p.targetConnectSessions.Delete(sessionKey)
}
p.AddCurrentConnections(-1)
p.log.Infof("[%s]目标地址[%s]关闭连接[%s]", p.GetKey(), tgConn.RemoteAddr().String(), tgConn.LocalAddr().String())
}()
var targetConn *net.UDPConn
p.AddCurrentConnections(1)
for {
targetConn = nil
session = nil
timeout := 1200 * time.Millisecond
if p.ReadFromTargetOnce() {
timeout = 300 * time.Millisecond
}
if p.ReadFromTargetOnce() {
targetConn = tgConn
} else {
se, ok := p.targetConnectSessions.Load(sessionKey)
if !ok {
return
}
session = se.(*udpTagetConSession)
targetConn = session.targetConn
}
targetConn.SetReadDeadline(time.Now().Add(timeout))
n, _, err := targetConn.ReadFromUDP(readBuffer)
if err != nil {
errStr := err.Error()
if strings.Contains(errStr, `i/o timeout`) && !p.ReadFromTargetOnce() {
continue
}
if !strings.Contains(errStr, `use of closed network connection`) {
p.log.Errorf("[%s]targetConn ReadFromUDP error:%s", p.GetKey(), err.Error())
}
return
}
data := pool.GetBuf(n)
copy(data, readBuffer[:n])
udpMsg := udpPackge{dataSize: n, data: &data, remoteAddr: raddr}
if err = errors.PanicToError(func() {
select {
case p.replyCh <- &udpMsg: //转发数据到远程地址
default:
}
}); err != nil {
return
}
if p.ReadFromTargetOnce() { //一次性
return
}
//非一次性,刷新时间或者退出
_, ok := p.targetConnectSessions.Load(sessionKey)
if !ok {
return
}
}
}
func (p *UDPProxy) Forwarder(kk int, replych chan *udpPackge) {
// read from targetAddr and write clientAddr
readFromtargetAddrFunc := func(raddr *net.UDPAddr, udpItemKey string, tgConn *net.UDPConn) {
readBuffer := pool.GetBuf(p.GetUDPPacketSize())
defer func() {
pool.PutBuf(readBuffer)
if p.ReadFromTargetOnce() {
tgConn.Close()
}
p.AddCurrentConnections(-1)
}()
var targetConn *net.UDPConn
var udpItem *udpMapItem
var ok bool
p.AddCurrentConnections(1)
for {
targetConn = nil
udpItem = nil
timeout := 1200 * time.Millisecond
if p.ReadFromTargetOnce() {
timeout = 30 * time.Millisecond
}
if p.ReadFromTargetOnce() {
targetConn = tgConn
} else {
p.targetudpConnItemMapMutex.RLock()
udpItem, ok = p.targetudpConnItemMap[udpItemKey]
if !ok {
p.targetudpConnItemMapMutex.RUnlock()
return
}
p.targetudpConnItemMapMutex.RUnlock()
targetConn = udpItem.conn
}
targetConn.SetReadDeadline(time.Now().Add(timeout))
n, _, err := targetConn.ReadFromUDP(readBuffer)
if err != nil {
errStr := err.Error()
if strings.Contains(errStr, `i/o timeout`) && !p.ReadFromTargetOnce() {
continue
}
if !strings.Contains(errStr, `use of closed network connection`) {
log.Printf("targetConn ReadFromUDP error:%s", err.Error())
}
return
}
data := pool.GetBuf(n)
copy(data, readBuffer[:n])
udpMsg := udpPackge{dataSize: n, data: &data, remoteAddr: raddr}
if err = errors.PanicToError(func() {
select {
case p.replyCh <- &udpMsg:
default:
}
}); err != nil {
return
}
if !p.ReadFromTargetOnce() {
p.targetudpConnItemMapMutex.Lock()
udpItem, ok := p.targetudpConnItemMap[udpItemKey]
if ok {
udpItem.lastTime = time.Now()
}
p.targetudpConnItemMapMutex.Unlock()
if !ok {
return
}
}
if p.ReadFromTargetOnce() {
return
}
}
}
var err error
var targetConn *net.UDPConn
// read from readCh
for udpMsg := range replych {
err = nil
targetConn = nil
se, ok := p.targetConnectSessions.Load(udpMsg.remoteAddr.String())
//if p.ReadFromTargetOnce()
p.targetudpConnItemMapMutex.Lock()
udpConnItem, ok := p.targetudpConnItemMap[udpMsg.remoteAddr.String()]
if !ok || p.ReadFromTargetOnce() { //??
tgAddr, err := net.ResolveUDPAddr(p.ProxyType, p.GetTargetAddress())
if !ok {
err := p.CheckReadTargetDataGoroutineLimit()
if err != nil {
log.Printf("net.ResolveUDPAddr[%s] error:%s", p.GetTargetAddress(), err.Error())
p.targetudpConnItemMapMutex.Unlock()
p.log.Warnf("[%s]转发中止:%s", p.GetKey(), err.Error())
continue
}
}
var session *udpTagetConSession
if ok {
session = se.(*udpTagetConSession)
} else {
session = &udpTagetConSession{}
}
if !ok {
addr := p.GetTargetAddress()
tgAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
p.log.Errorf("[%s]UDP端口转发目标地址[%s]解析出错:%s", p.GetKey(), addr, err.Error())
pool.PutBuf(*udpMsg.data)
continue
}
targetConn, err = net.DialUDP("udp", nil, tgAddr)
targetConn, err := net.DialUDP("udp", nil, tgAddr)
if err != nil {
p.targetudpConnItemMapMutex.Unlock()
p.log.Errorf("[%s]UDP端口转发目标地址[%s]连接出错:%s", p.GetKey(), addr, err.Error())
pool.PutBuf(*udpMsg.data)
continue
}
targetConn.SetWriteBuffer(4 * 1024 * 1024)
targetConn.SetReadBuffer(4 * 1024 * 1024)
if !ok && !p.ReadFromTargetOnce() {
p.AddCurrentConnections(1)
newItem := udpMapItem{conn: targetConn, lastTime: time.Now()}
p.targetudpConnItemMap[udpMsg.remoteAddr.String()] = &newItem
udpConnItem = &newItem
}
} else {
udpConnItem.lastTime = time.Now()
targetConn = udpConnItem.conn
session.targetConn = targetConn
}
p.targetudpConnItemMapMutex.Unlock()
session.lastTime = time.Now()
p.ReceiveDataCallback(int64(udpMsg.dataSize))
_, err = targetConn.Write(*udpMsg.data)
if !p.ReadFromTargetOnce() { //只存储非一次性
p.targetConnectSessions.Store(udpMsg.remoteAddr.String(), session)
}
p.ReceiveDataCallback(int64(udpMsg.dataSize)) //接收流量记录
_, err = session.targetConn.Write(*udpMsg.data)
if err != nil {
targetConn.Close()
p.log.Errorf("[%s]转发数据到目标端口出错:%s", p.GetKey(), err.Error())
session.targetConn.Close()
continue
}
pool.PutBuf(*udpMsg.data)
if !ok || p.ReadFromTargetOnce() {
go readFromtargetAddrFunc(udpMsg.remoteAddr, udpMsg.remoteAddr.String(), targetConn)
if !ok {
go p.handlerDataFromTargetAddress(udpMsg.remoteAddr, session.targetConn)
}
}
@ -391,43 +382,42 @@ func (p *UDPProxy) replyDataToRemotAddress() {
_, err := p.listenConn.WriteToUDP(*(msg.data), msg.remoteAddr)
pool.PutBuf(*msg.data)
if err != nil {
log.Printf("udpConn.WriteToUDP error:%s", err.Error())
p.log.Errorf("[%s]转发目标端口数据到远程端口出错:%s", p.GetKey(), err.Error())
continue
}
p.SendDataCallback(int64(msg.dataSize))
p.SendDataCallback(int64(msg.dataSize)) //发送流量记录
}
}
func (p *UDPProxy) CheckTargetUDPConn() {
func (p *UDPProxy) CheckReadTargetDataGoroutineLimit() error {
if GetGlobalUDPPortForwardGroutineCount() >= GetGlobalUDPReadTargetDataMaxgoroutineCountLimit() {
return fmt.Errorf("超出端口转发全局UDP读取目标地址数据协程数限制[%d]", GetGlobalUDPReadTargetDataMaxgoroutineCountLimit())
}
if p.GetCurrentConnections() >= p.SingleProxyMaxUDPReadTargetDatagoroutineCount {
return fmt.Errorf("超出单端口UDP读取目标地址数据协程数限制[%d]", p.SingleProxyMaxUDPReadTargetDatagoroutineCount)
}
return nil
}
func (p *UDPProxy) CheckTargetUDPConnectSessions() {
for {
<-time.After(time.Second * 1)
// connCout := atomic.LoadInt64(&p.targetudpConnCount)
// if connCout <= 0 {
// continue
// }
if p.isStop {
return
}
if p.GetCurrentConnections() <= 0 {
continue
}
p.targetudpConnItemMapMutex.Lock()
var deleteList []string
for k, v := range p.targetudpConnItemMap {
if time.Since(v.lastTime) >= 30*time.Second {
v.conn.Close()
deleteList = append(deleteList, k)
p.targetConnectSessions.Range(func(key any, value any) bool {
session := value.(*udpTagetConSession)
if time.Since(session.lastTime) >= 30*time.Second {
session.targetConn.Close()
p.targetConnectSessions.Delete(key)
}
}
return true
})
//fmt.Printf("map:%v\t deleteList:%v\n", p.targetudpConnItemMap, deleteList)
for i := range deleteList {
delete(p.targetudpConnItemMap, deleteList[i])
//log.Printf("删除targetudpConnItemMap [%s]\n", deleteList[i])
//atomic.AddInt64(&p.targetudpConnCount, -1)
p.AddCurrentConnections(-1)
}
p.targetudpConnItemMapMutex.Unlock()
}
}

View File

@ -0,0 +1,101 @@
package blinker
import "fmt"
const (
VA_TYPE_LIGHT = "light"
VA_TYPE_OUTLET = "outlet"
VA_TYPE_MULTI_OUTLET = "multi_outlet"
VA_TYPE_SENSOR = "sensor"
VA_TYPE_FAN = "fan"
VA_TYPE_AIRCONDITION = "aircondition"
)
type VoiceAssistant struct {
DeviceType string //语言助手类型 (设备类型).
VAType string //语言助手类型 MIOT AliGenie DuerOS
Device *BlinkerDevice
topic string
}
func (v *VoiceAssistant) GetSKey() string {
switch v.VAType {
case "MIOT":
return "miType"
case "AliGenie":
return "aliType"
case "DuerOS":
return "duerType"
default:
return ""
}
}
func (v *VoiceAssistant) PowerChangeReply(msgid, st string) {
state := "off"
if st == "true" {
state = "on"
}
// if v.VAType == "MIOT" {
// if state == "on" {
// state = "true"
// } else {
// state = "false"
// }
// }
data := map[string]string{"pState": state}
v.Device.SendMessage("vAssistant", v.GetToDevice(), msgid, data)
}
func (v *VoiceAssistant) QueryDeviceState(msgid string) {
state := v.Device.state
// if v.VAType == "MIOT" {
// if state == "on" {
// state = "true"
// } else {
// state = "false"
// }
// }
data := map[string]string{"pState": state}
v.Device.SendMessage("vAssistant", v.GetToDevice(), msgid, data)
}
func (v *VoiceAssistant) GetToDevice() string {
// if v.Device.DetailInfo.Broker == "blinker" {
// return "ServerReceiver"
// }
return v.topic
}
func CreateVoiceAssistant(deviceType, vaType string) *VoiceAssistant {
switch vaType {
case "MIOT":
return &VoiceAssistant{DeviceType: deviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)}
case "AliGenie":
return &VoiceAssistant{DeviceType: deviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)}
case "DuerOS":
{
newDeviceType := ""
switch deviceType {
case VA_TYPE_LIGHT:
newDeviceType = "LIGHT"
case VA_TYPE_OUTLET:
newDeviceType = "SOCKET"
case VA_TYPE_MULTI_OUTLET:
newDeviceType = "MULTI_SOCKET"
case VA_TYPE_SENSOR:
newDeviceType = "AIR_MONITOR"
default:
}
if newDeviceType == "" {
return nil
}
return &VoiceAssistant{DeviceType: newDeviceType, VAType: vaType, topic: fmt.Sprintf("%s_r", vaType)}
}
default:
return nil
}
}

View File

@ -0,0 +1,439 @@
package blinker
import (
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"sync"
"time"
"github.com/buger/jsonparser"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
const (
HOST = "https://iot.diandeng.tech"
API_AUTH = HOST + "/api/v1/user/device/diy/auth"
API_HEARTBEAT = HOST + "/api/v1/user/device/heartbeat"
API_VOICE_ASSISTANT = HOST + "/api/v1/user/device/voice_assistant"
)
type BlinkerDevice struct {
authKey string
subTopic string
pubTopic string
exasubTopic string //aliyun特有
exapubTopic string //aliyun特有
client MQTT.Client
DetailInfo BlinkerDetailInfo
heartBeatChan chan uint8
hbmu sync.Mutex
preSendTime time.Time
sendMsgChan chan message
//
state string
voiceAssistants map[string]*VoiceAssistant
}
type message struct {
TargetType string
Device string
MessageID string
Msg any
}
func CreateBlinkerDevice(ak string) *BlinkerDevice {
d := &BlinkerDevice{authKey: ak}
d.voiceAssistants = make(map[string]*VoiceAssistant)
d.state = "on"
return d
}
func (d *BlinkerDevice) AddVoiceAssistant(v *VoiceAssistant) {
v.Device = d
d.voiceAssistants[v.VAType] = v
}
func (d *BlinkerDevice) SyncAssistants() error {
for _, v := range d.voiceAssistants {
skey := v.GetSKey()
dataMap := make(map[string]string)
dataMap["token"] = d.DetailInfo.IotToken
dataMap[skey] = v.DeviceType
dataBytes, _ := json.Marshal(dataMap)
resp, err := http.Post(API_VOICE_ASSISTANT, "application/json", strings.NewReader(string(dataBytes)))
if err != nil {
return err
}
_, err = GetBytesFromHttpResponse(resp)
if err != nil {
return err
}
//fmt.Printf("同步语音助手结果:%s\n", respBytes)
}
return nil
}
func (d *BlinkerDevice) RunSenderMessageService() {
for m := range d.sendMsgChan {
t := time.Since(d.preSendTime) - time.Millisecond*1100
if t < 0 {
//log.Printf("太快,睡眠一下:%d\n", -t)
<-time.After(-t)
}
d.sendMessage(m.TargetType, m.Device, m.MessageID, m.Msg)
}
}
func (d *BlinkerDevice) RunHeartBearTimer() {
if !d.hbmu.TryLock() {
return
}
defer d.hbmu.Unlock()
log.Printf("开始心跳...\n")
d.heartBeatChan <- uint8(1)
for range d.heartBeatChan {
d.heartBeat()
<-time.After(time.Second * 599)
d.heartBeatChan <- uint8(1)
}
log.Printf("心跳中止...\n")
}
func (d *BlinkerDevice) Init() error {
apiurl := fmt.Sprintf("%s?authKey=%s", API_AUTH, d.authKey)
resp, err := http.Get(apiurl)
if err != nil {
return fmt.Errorf("device init http.Get err:%s", err.Error())
}
var infoRes BlinkerInfoRes
err = GetAndParseJSONResponseFromHttpResponse(resp, &infoRes)
if err != nil {
return fmt.Errorf("parse DeviceInfo resp err:%s", err.Error())
}
d.DetailInfo = infoRes.Detail
err = d.SyncAssistants()
if err != nil {
return err
}
// if d.DetailInfo.Broker == "blinker" {
// d.subTopic = fmt.Sprintf("/device/%s/r", d.DetailInfo.DeviceName)
// d.pubTopic = fmt.Sprintf("/device/%s/s", d.DetailInfo.DeviceName)
// return nil
// }
// if d.DetailInfo.Broker == "aliyun" {
// d.subTopic = fmt.Sprintf("/%s/%s/r", d.DetailInfo.ProductKey, d.DetailInfo.DeviceName)
// d.pubTopic = fmt.Sprintf("/%s/%s/s", d.DetailInfo.ProductKey, d.DetailInfo.DeviceName)
// d.exasubTopic = "/device/ServerSender/r"
// d.exapubTopic = "/device/ServerReceiver/s"
// return nil
// }
d.subTopic = fmt.Sprintf("/device/%s/r", d.DetailInfo.DeviceName)
d.pubTopic = fmt.Sprintf("/device/%s/s", d.DetailInfo.DeviceName)
d.exasubTopic = "/device/ServerSender/r"
d.exapubTopic = "/device/ServerReceiver/s"
return nil
}
func (d *BlinkerDevice) Login() error {
opts := MQTT.NewClientOptions()
brokeyURL := fmt.Sprintf("%s:%s", d.DetailInfo.Host, d.DetailInfo.Port)
//brokeyURL := fmt.Sprintf("tcp://broker.diandeng.tech:%s", d.DetailInfo.Port)
opts.AddBroker(brokeyURL)
opts.SetClientID(d.DetailInfo.DeviceName)
opts.SetUsername(d.DetailInfo.IotID)
opts.SetPassword(d.DetailInfo.IotToken)
//opts.SetKeepAlive(time.Second * 3)
//opts.WillRetained = true
//choke := make(chan [2]string)
// opts.SetDefaultPublishHandler(func(client MQTT.Client, msg MQTT.Message) {
// //choke <- [2]string{msg.Topic(), string(msg.Payload())}
// msg.Payload()
// })
opts.SetOnConnectHandler(func(c MQTT.Client) {
log.Printf("连接成功!")
d.client = c
c.Subscribe(d.subTopic, byte(0), d.ReceiveMessageHandler)
//c.Subscribe(d.exasubTopic, byte(0), d.ReceiveMessageHandler)
d.heartBeatChan = make(chan uint8, 1)
go d.RunHeartBearTimer()
d.sendMsgChan = make(chan message, 8)
go d.RunSenderMessageService()
})
opts.OnConnectionLost = func(c MQTT.Client, err error) {
log.Printf("连接丢失:%s\n", err.Error())
close(d.heartBeatChan)
close(d.sendMsgChan)
d.client = nil
}
//opts.
client := MQTT.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
return fmt.Errorf("连接出错:%s", token.Error())
}
<-time.After(time.Second * 60000)
return nil
}
func (d *BlinkerDevice) heartBeat() error {
//hr := fmt.Sprintf("%s?deviceName=%s&key=%s&heartbeat=600", SERVER+HEARTBEAT_URL, d.DetailInfo.DeviceName, d.authKey)
hr := fmt.Sprintf("%s?deviceName=%s&key=%s&heartbeat=600", API_HEARTBEAT, d.DetailInfo.DeviceName, d.authKey)
resp, err := http.Get(hr)
if err != nil {
return fmt.Errorf("device init http.Get err:%s", err.Error())
}
respBytes, err := GetBytesFromHttpResponse(resp)
if err != nil {
return err
}
fmt.Printf("HearBeat:%s\n", string(respBytes))
return nil
}
func (d *BlinkerDevice) ReceiveMessageHandler(c MQTT.Client, m MQTT.Message) {
log.Printf("接收到MQTT消息:【%s】%s\n", m.Topic(), m.Payload())
if m.Topic() != d.subTopic {
return
}
fromDevice, fromDeviceErr := jsonparser.GetString(m.Payload(), "fromDevice")
if fromDeviceErr != nil || (fromDevice != d.DetailInfo.UUID && fromDevice != "ServerSender") {
return
}
if fromDevice == d.DetailInfo.UUID {
d.ownAppMessagehandler(m.Payload())
return
}
from, fromErr := jsonparser.GetString(m.Payload(), "data", "from")
if fromErr != nil {
return
}
switch from {
case "MIOT", "AliGenie", "DuerOS":
d.voiceAssistantMessageHandler(from, m.Payload())
default:
}
}
func (d *BlinkerDevice) voiceAssistantMessageHandler(from string, msg []byte) {
fmt.Printf("from:%s msg:%s\n", from, string(msg))
va, ok := d.voiceAssistants[from]
if !ok {
return
}
//fmt.Printf("voiceAssistantMessageHandler\t msg:[%s]\n", msg)
messageId, messageIdErr := jsonparser.GetString(msg, "data", "messageId")
if messageIdErr != nil {
return
}
//jsonparser.GetString(msg, "data", "set")
pstate, pstateErr := jsonparser.GetString(msg, "data", "set", "pState")
if pstateErr == nil {
d.powerChange(va, messageId, pstate)
return
}
_, getKeyerr := jsonparser.GetString(msg, "data", "get")
if getKeyerr == nil {
va.QueryDeviceState(messageId)
}
//va.Power()
}
func (d *BlinkerDevice) powerChange(va *VoiceAssistant, msgId, state string) {
d.state = state
if va != nil {
va.PowerChangeReply(msgId, state)
}
}
func (d *BlinkerDevice) ownAppMessagehandler(msg []byte) {
getValue, getKeyError := jsonparser.GetString(msg, "data", "get")
if getKeyError == nil {
switch getValue {
case "state":
d.SendMessage("OwnApp", d.DetailInfo.UUID, "", map[string]any{"state": "online"})
case "timing":
d.SendMessage("OwnApp", d.DetailInfo.UUID, "", map[string]any{"timing": map[string]any{"timing": []any{}}}) //{"timing":{"timing":[]}}
case "countdown":
d.SendMessage("OwnApp", d.DetailInfo.UUID, "", map[string]any{"countdown": "false"}) //`{ "countdown": false }`
default:
fmt.Printf(` "data", "get":Value:%s`, getValue)
}
return
}
}
type mess2device struct {
DeviceType string `json:"deviceType"`
Data any `json:"data"`
FromDeivce string `json:"fromDevice"`
ToDevice string `json:"toDevice"`
}
type mess2assistant struct {
DeviceType string `json:"deviceType"`
Data any `json:"data"`
FromDeivce string `json:"fromDevice"`
ToDevice string `json:"toDevice"`
MessageID string `json:"-"` //`json:"messageId"`
}
func (d *BlinkerDevice) formatMess2assistant(targetType, toDevice, msgid string, data any) ([]byte, error) {
m := mess2assistant{DeviceType: targetType, Data: data, FromDeivce: d.DetailInfo.DeviceName, ToDevice: toDevice, MessageID: msgid}
rawBytes, err := json.Marshal(m)
if err != nil {
return []byte{}, err
}
str := base64.StdEncoding.EncodeToString(rawBytes)
log.Printf("回复语音助手:%s\n", string(rawBytes))
//fmt.Printf("base64:%s\n", str)
return []byte(str), nil
//return rawBytes, nil
}
func (d *BlinkerDevice) formatMess2Device(targetType, toDevice string, data any) ([]byte, error) {
m := mess2device{DeviceType: targetType, Data: data, FromDeivce: d.DetailInfo.DeviceName, ToDevice: toDevice}
return json.Marshal(m)
}
func (d *BlinkerDevice) SendMessage(targetType, todevice, msgid string, msg any) {
m := message{Device: todevice, Msg: msg, TargetType: targetType, MessageID: msgid}
d.sendMsgChan <- m
}
func (d *BlinkerDevice) sendMessage(targetType, todevice, msgid string, msg any) error {
if d.client == nil {
return fmt.Errorf("SendMessage error:client == nil")
}
var pubTopic string
var payload []byte
var err error
if targetType == "OwnApp" {
pubTopic = d.pubTopic
payload, err = d.formatMess2Device(targetType, todevice, msg)
if err != nil {
return err
}
} else if targetType == "vAssistant" {
//pubTopic = "/device/ServerReceiver/s"
//pubTopic = fmt.Sprintf("/sys/%s/%s/rrpc/response/%s", d.DetailInfo.ProductKey, d.DetailInfo.DeviceName, msgid)
//pubTopic = fmt.Sprintf("%s", d.exapubTopic)
pubTopic = d.pubTopic
payload, err = d.formatMess2assistant(targetType, todevice, msgid, msg)
if err != nil {
return err
}
}
fmt.Printf("topic:%s\n", pubTopic)
if token := d.client.Publish(pubTopic, 0, false, payload); token.Wait() && token.Error() != nil {
fmt.Printf("Publish error:%s\n", token.Error())
return token.Error()
}
d.preSendTime = time.Now()
return nil
}
//-----------------------
type BlinkerDetailInfo struct {
Broker string `json:"broker"`
DeviceName string `json:"deviceName"`
Host string `json:"host"`
IotID string `json:"iotId"`
IotToken string `json:"iotToken"`
Port string `json:"port"`
ProductKey string `json:"productKey"`
UUID string `json:"uuid"`
}
type BlinkerInfoRes struct {
Message int `json:"message"`
Detail BlinkerDetailInfo `json:"detail"`
}
// GetStringFromHttpResponse 从response获取
func GetBytesFromHttpResponse(resp *http.Response) ([]byte, error) {
if resp == nil || resp.Body == nil {
return []byte{}, fmt.Errorf("resp.Body = nil")
}
defer resp.Body.Close()
var body []byte
var err error
if resp.Header.Get("Content-Encoding") == "gzip" {
reader, err := gzip.NewReader(resp.Body)
if err != nil {
return []byte{}, err
}
body, err = ioutil.ReadAll(reader)
return body, err
}
body, err = ioutil.ReadAll(resp.Body)
return body, err
}
func GetAndParseJSONResponseFromHttpResponse(resp *http.Response, result interface{}) error {
bytes, err := GetBytesFromHttpResponse(resp)
if err != nil {
return fmt.Errorf("GetBytesFromHttpResponse err:%s", err.Error())
}
if len(bytes) > 0 {
err = json.Unmarshal(bytes, &result)
if err != nil {
//log.Printf("请求接口解析json结果失败! ERROR: %s\n", err)
return fmt.Errorf("GetAndParseJSONResponseFromHttpResponse 解析JSON结果出错%s", err.Error())
}
}
return nil
}

View File

@ -12,6 +12,7 @@ import (
"net/url"
"strings"
"sync"
"syscall"
"time"
"golang.org/x/net/proxy"
@ -239,6 +240,13 @@ func NewTransport(transportNetwork,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
LocalAddr: localAddr,
Control: func(network, address string, c syscall.RawConn) error {
// fmt.Printf("network:%s\taddress:%s\n", network, address)
if network != transportNetwork && transportNetwork != "tcp" {
return fmt.Errorf("must use :%s", transportNetwork)
}
return nil
},
})
transport = &http.Transport{

View File

@ -49,9 +49,10 @@ func (l *LogsBuffer) Fire(entry *logrus.Entry) error {
return fmt.Errorf("entry.String() err:%s", err.Error())
}
l.AddLog(entry.Time, entryStr, entry.Data)
if l.fireCallback != nil {
return l.fireCallback(entry)
} else {
l.AddLog(entry.Time, entryStr, entry.Data)
}
return nil
@ -169,3 +170,30 @@ func (l *LogsBuffer) GetLogCount() int {
defer l.mu.Unlock()
return len(l.logsStore)
}
//---------------------------
var LogsBufferStore map[string]*LogsBuffer
var LogsBufferStoreMu sync.Mutex
func init() {
LogsBufferStore = make(map[string]*LogsBuffer)
}
func CreateLogbuffer(key string, buffSize int) *LogsBuffer {
if strings.TrimSpace(key) == "" {
return nil
}
LogsBufferStoreMu.Lock()
defer LogsBufferStoreMu.Unlock()
var buf *LogsBuffer
var ok bool
if buf, ok = LogsBufferStore[key]; !ok {
buf = &LogsBuffer{}
buf.SetBufferSize(buffSize)
LogsBufferStore[key] = buf
} else if buf.GetBufferSize() != buffSize {
buf.SetBufferSize(buffSize)
}
return buf
}

View File

@ -152,3 +152,45 @@ func GetIPFromNetInterface(ipType, netinterface, ipreg string) string {
return ""
}
func GetGlobalIPv4BroadcastList() []string {
var res []string
allNetInterfaces, err := net.Interfaces()
if err != nil {
return res
}
for i := 0; i < len(allNetInterfaces); i++ {
if (allNetInterfaces[i].Flags & net.FlagUp) != 0 {
addrs, _ := allNetInterfaces[i].Addrs()
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
_, bits := ipnet.Mask.Size()
// 需匹配全局单播地址
//if bits == 128 && ipv6Unicast.Contains(ipnet.IP) {
if bits == 32 {
//ipv4 = append(ipv4, ipnet.IP.String())
bcst := GetBroadcast(ipnet.IP, ipnet.Mask)
res = append(res, bcst)
}
}
}
}
}
return res
}
func GetBroadcast(ip net.IP, mask net.IPMask) string {
bcst := make(net.IP, len(ip))
copy(bcst, ip)
for i := 0; i < len(mask); i++ {
ipIdx := len(bcst) - i - 1
bcst[ipIdx] = ip[ipIdx] | ^mask[len(mask)-i-1]
}
return bcst.String()
}

View File

@ -0,0 +1,12 @@
package stringsp
func StrIsInList(str string, strList []string) bool {
checkMap := make(map[string]uint8)
for i := range strList {
checkMap[strList[i]] = 1
}
if _, ok := checkMap[str]; ok {
return true
}
return false
}

View File

@ -0,0 +1,74 @@
package wol
////////////////////////////////////////////////////////////////////////////////
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"regexp"
)
////////////////////////////////////////////////////////////////////////////////
var (
delims = ":-"
reMAC = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`)
)
////////////////////////////////////////////////////////////////////////////////
// MACAddress represents a 6 byte network mac address.
type MACAddress [6]byte
// MagicPacket is constituted of 6 bytes of 0xFF followed by 16-groups of the
// destination MAC address.
type MagicPacket struct {
header [6]byte
payload [16]MACAddress
}
// New returns a magic packet based on a mac address string.
func New(mac string) (*MagicPacket, error) {
var packet MagicPacket
var macAddr MACAddress
hwAddr, err := net.ParseMAC(mac)
if err != nil {
return nil, err
}
// We only support 6 byte MAC addresses since it is much harder to use the
// binary.Write(...) interface when the size of the MagicPacket is dynamic.
if !reMAC.MatchString(mac) {
return nil, fmt.Errorf("%s is not a IEEE 802 MAC-48 address", mac)
}
// Copy bytes from the returned HardwareAddr -> a fixed size MACAddress.
for idx := range macAddr {
macAddr[idx] = hwAddr[idx]
}
// Setup the header which is 6 repetitions of 0xFF.
for idx := range packet.header {
packet.header[idx] = 0xFF
}
// Setup the payload which is 16 repetitions of the MAC addr.
for idx := range packet.payload {
packet.payload[idx] = macAddr
}
return &packet, nil
}
// Marshal serializes the magic packet structure into a 102 byte slice.
func (mp *MagicPacket) Marshal() ([]byte, error) {
var buf bytes.Buffer
if err := binary.Write(&buf, binary.BigEndian, mp); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

94
thirdlib/go-wol/wol.go Normal file
View File

@ -0,0 +1,94 @@
package wol
import (
"fmt"
"net"
)
func WakeUpRepeat(macAddr, broadcastIP, bcastInterface string, port, repeat int) {
i := 0
for {
WakeUp(macAddr, broadcastIP, bcastInterface, port)
i++
if i >= repeat {
return
}
}
}
func WakeUp(macAddr, broadcastIP, bcastInterface string, port int) error {
var localAddr *net.UDPAddr
var err error
if bcastInterface != "" {
localAddr, err = ipFromInterface(bcastInterface)
if err != nil {
return err
}
}
bcastAddr := fmt.Sprintf("%s:%d", broadcastIP, port)
udpAddr, err := net.ResolveUDPAddr("udp", bcastAddr)
if err != nil {
return err
}
// Build the magic packet.
mp, err := New(macAddr)
if err != nil {
return err
}
bs, err := mp.Marshal()
if err != nil {
return err
}
conn, err := net.DialUDP("udp", localAddr, udpAddr)
if err != nil {
return err
}
defer conn.Close()
//fmt.Printf("Attempting to send a magic packet to MAC %s\n", macAddr)
//fmt.Printf("... Broadcasting to: %s\n", bcastAddr)
n, err := conn.Write(bs)
if err == nil && n != 102 {
err = fmt.Errorf("magic packet sent was %d bytes (expected 102 bytes sent)", n)
}
if err != nil {
return err
}
//fmt.Printf("Magic packet sent successfully to %s\n", macAddr)
return nil
}
// ipFromInterface returns a `*net.UDPAddr` from a network interface name.
func ipFromInterface(iface string) (*net.UDPAddr, error) {
ief, err := net.InterfaceByName(iface)
if err != nil {
return nil, err
}
addrs, err := ief.Addrs()
if err == nil && len(addrs) <= 0 {
err = fmt.Errorf("no address associated with interface %s", iface)
}
if err != nil {
return nil, err
}
// Validate that one of the addrs is a valid network IP address.
for _, addr := range addrs {
switch ip := addr.(type) {
case *net.IPNet:
if !ip.IP.IsLoopback() && ip.IP.To4() != nil {
return &net.UDPAddr{
IP: ip.IP,
}, nil
}
}
}
return nil, fmt.Errorf("no address associated with interface %s", iface)
}

13
web.go
View File

@ -1,16 +1,11 @@
//go:build adminweb
// +build adminweb
package main
import (
"fmt"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/web"
"log"
)
func RunAdminWeb(listenPort, logMaxSize int) {
listen := fmt.Sprintf(":%d", listenPort)
go web.RunAdminWeb(listen, logMaxSize)
log.Printf("AdminWeb listen on %s", listen)
func RunAdminWeb(conf *config.BaseConfigure) {
//listen := fmt.Sprintf(":%d", listenPort)
go web.RunAdminWeb(conf)
}

View File

@ -46,12 +46,15 @@ declare module '@vue/runtime-core' {
Log: typeof import('./src/components/Log.vue')['default']
Login: typeof import('./src/components/Login.vue')['default']
Pmenu: typeof import('./src/components/Pmenu.vue')['default']
PortForward: typeof import('./src/components/PortForward.vue')['default']
PortForwardSet: typeof import('./src/components/PortForwardSet.vue')['default']
PSet: typeof import('./src/components/PSet.vue')['default']
RelaySet: typeof import('./src/components/RelaySet.vue')['default']
ReverseProxy: typeof import('./src/components/ReverseProxy.vue')['default']
SSL: typeof import('./src/components/SSL.vue')['default']
Status: typeof import('./src/components/Status.vue')['default']
WhiteLists: typeof import('./src/components/WhiteLists.vue')['default']
WhiteListSet: typeof import('./src/components/WhiteListSet.vue')['default']
WOL: typeof import('./src/components/tools/WOL.vue')['default']
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,8 @@
<title>Lucky</title>
<script type="module" crossorigin src="/assets/index.2c06576c.js"></script>
<link rel="stylesheet" href="/assets/index.6859b28a.css">
<script type="module" crossorigin src="/assets/index.e5c8aec2.js"></script>
<link rel="stylesheet" href="/assets/index.f23c7bd8.css">
</head>
<body style="margin:0">
<div id="app"></div>

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,6 @@
<el-main id="pageContent">
<Log v-if="global.currentPage.value=='#log'?true:false"></Log>
<Status v-if="global.currentPage.value=='#status'?true:false"></Status>
<Relayset v-if="global.currentPage.value=='#relayset'?true:false"></Relayset>
<Pset v-if="global.currentPage.value=='#set'?true:false"></Pset>
<Login v-if="global.currentPage.value=='#login'?true:false"></Login>
<WhiteListSet v-if="global.currentPage.value=='#whitelistset'?true:false"></WhiteListSet>
@ -26,7 +25,11 @@
<DDNS v-if="global.currentPage.value=='#ddnstasklist'?true:false"></DDNS>
<DDNSSet v-if="global.currentPage.value=='#ddnsset'?true:false"></DDNSSet>
<ReverseProxy v-if="global.currentPage.value=='#reverseproxylist'?true:false"></ReverseProxy>
<SSL v-if="global.currentPage.value=='#ssl'?true:false"></SSL>
<About v-if="global.currentPage.value=='#about'?true:false"></About>
<PortForward v-if="global.currentPage.value=='#portforward'"></PortForward>
<PortForwardSet v-if="global.currentPage.value=='#portforwardset'"></PortForwardSet>
<WOL v-if="global.currentPage.value=='#wol'"></WOL>
</el-main>
</el-container>
@ -43,7 +46,6 @@ import { onMounted,ref,inject ,computed } from 'vue'
import Status from './components/status.vue'
import Log from './components/log.vue';
import Pmenu from './components/pmenu.vue';
import Relayset from './components/relayset.vue';
import Pset from './components/pset.vue';
import Login from './components/login.vue';
import WhiteListSet from './components/WhiteListSet.vue';
@ -52,9 +54,10 @@ import BlackLists from './components/BlackLists.vue';
import DDNS from './components/DDNS.vue';
import ReverseProxy from './components/reverseproxy.vue';
import {apiGetVersion} from "./apis/utils.js"
import DDNSSet from './components/DDNSSet.vue';
import SSL from './components/SSL.vue'
//console.log("111")

View File

@ -339,6 +339,45 @@ export function apiGetConfigure() {
})
}
export function apiDeleteSSL(key) {
return httpRequest({
url: '/api/ssl',
method: 'delete',
method: 'delete',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf(),key:key}
})
}
export function apiAddSSL(data) {
return httpRequest({
url: '/api/ssl',
method: 'post',
headers:{'Authorization':GetToken()},
data:data
})
}
export function apiAlterSSL(key,field,value) {
return httpRequest({
url: '/api/ssl',
method: 'put',
headers:{'Authorization':GetToken()},
params:{key:key,field:field,value:value},
})
}
export function apiGetSSLList(data) {
return httpRequest({
url: '/api/ssl',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf()}
})
}
export function apiAddReverseProxyRule(data) {
return httpRequest({
@ -404,3 +443,130 @@ export function apiReverseProxyRuleLogs(ruleKey,proxyKey,pageSize,page) {
page:page}
})
}
//-------------------------------------------------
export function apiGetPortForwardRuleList() {
return httpRequest({
url: '/api/portforwards',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf()}
})
}
export function apiAddPortForwardRule(data) {
return httpRequest({
url: '/api/portforward',
method: 'post',
headers:{'Authorization':GetToken()},
data:data
})
}
export function apiDeletePortForwardRule(key) {
return httpRequest({
url: '/api/portforward',
method: 'delete',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf(),key:key}
})
}
export function apiAlterPortForwardRule(data) {
return httpRequest({
url: '/api/portforward',
method: 'put',
headers:{'Authorization':GetToken()},
data:data
})
}
export function apiPortForwardRuleEnable(key,enable) {
return httpRequest({
url: '/api/portforward/enable',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf(),enable:enable,key:key}
})
}
export function apiQueryPortForwardConfigure() {
return httpRequest({
url: '/api/portforward/configure',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf()}
})
}
export function apiAlterPortForwardConfigure(data) {
return httpRequest({
url: '/api/portforward/configure',
method: 'put',
headers:{'Authorization':GetToken()},
data:data
})
}
export function apiPortforwardRuleLogs(key,pageSize,page) {
return httpRequest({
url: '/api/portforward/logs',
method: 'get',
headers:{'Authorization':GetToken()},
params:{
_:new Date().valueOf(),
key:key,
pageSize:pageSize,
page:page}
})
}
//-----------------
export function apiGetWOLDeviceList() {
return httpRequest({
url: '/api/wol/devices',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf()}
})
}
export function apiAddWOLDevice(data) {
return httpRequest({
url: '/api/wol/device',
method: 'post',
headers:{'Authorization':GetToken()},
data:data
})
}
export function apiDeleteWOLDevice(key) {
return httpRequest({
url: '/api/wol/device',
method: 'delete',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf(),key:key}
})
}
export function apiWOLDeviceWakeUp(key) {
return httpRequest({
url: '/api/wol/device/wakeup',
method: 'get',
headers:{'Authorization':GetToken()},
params:{_:new Date().valueOf(),key:key}
})
}
export function apiAlterWOLDevice(data) {
return httpRequest({
url: '/api/wol/device',
method: 'put',
headers:{'Authorization':GetToken()},
data:data
})
}

View File

@ -17,11 +17,15 @@
<div class="line">
<el-link type="primary" href="tencent://message/?uin=272288814&Site=&Menu=yes" target="_blank">QQ联系作者</el-link>
&nbsp;&nbsp;&nbsp;邮箱: 272288814@qq.com
<el-link type="primary" href="tencent://message/?uin=272288813&Site=&Menu=yes" target="_blank">QQ联系作者</el-link>
&nbsp;&nbsp;&nbsp;邮箱: 272288813@qq.com
</div>
<div class="line">
Lukcy交流 QQ群:&nbsp;&nbsp;602427029
</div>
<div class="line">
Github&nbsp;&nbsp;<el-link type="primary" href="https://github.com/gdy666/lucky" target="_blank">https://github.com/gdy666/lucky</el-link>
</div>
@ -30,10 +34,6 @@
Gitee&nbsp;&nbsp;<el-link type="primary" href="https://gitee.com/gdy666/lucky" target="_blank">https://gitee.com/gdy666/lucky</el-link>
</div>
<div class="line">
<el-link type="primary" href="https://pan.baidu.com/s/1NfumD9XjYU3OTeVmbu6vOQ?pwd=6666" target="_blank">最新版本可访问百度网盘</el-link>
</div>
<div>
本项目借鉴引用或参考的第三方开源项目: <el-link type="primary" href="https://github.com/fatedier/frp" target="_blank">frp</el-link> <el-link type="primary" href="https://github.com/jeessy2/ddns-go" target="_blank">ddns-go</el-link>
</div>

View File

@ -128,10 +128,10 @@ const flushBlackListlife = (index, ip, life) => {
const addBlackList = () => {
if (!isIP(addBlackListForm.value.IP)) {
MessageShow("error", "IP格式有误,请检查修正后再添加")
return
}
// if (!isIP(addBlackListForm.value.IP)) {
// MessageShow("error", "IP格式有误,请检查修正后再添加")
// return
// }
apiFlushBlackList(addBlackListForm.value.IP, addBlackListForm.value.Life).then((res) => {
if (res.ret == 0) {

View File

@ -111,7 +111,7 @@
</template>
<el-button color="#409eff" size="default">
{{ task.TaskState.WebhookCallTime == "" ? '从未触发' :
task.TaskState.WebhookCallTime
task.TaskState.WebhookCallTime
}}
</el-button>
</el-tooltip>
@ -172,7 +172,8 @@
<el-descriptions-item label="域名">
<el-button color="#409eff" size="default"
@click="copyDomain(domain.SubDomain, domain.DomainName)">
{{ domain.SubDomain == '' ? domain.DomainName : domain.SubDomain + "." + domain.DomainName }}
{{ domain.SubDomain == '' ? domain.DomainName : domain.SubDomain + "." +
domain.DomainName }}
</el-button>
</el-descriptions-item>
@ -185,7 +186,8 @@
</template>
<el-button :type="domain.UpdateStatus == '失败' ? 'danger' : task.Enable ? 'success' : 'info'"
<el-button
:type="domain.UpdateStatus == '失败' ? 'danger' : task.Enable ? 'success' : 'info'"
size="small">
{{ task.Enable ? domain.UpdateStatus : '停止同步' }}
</el-button>
@ -571,6 +573,17 @@
</div>
<p>DNS接口调用额外设置</p>
<div class="fromitemChildDivRadius">
<el-form-item label="DNS接口调用使用的网络类型" label-width="auto">
<el-select v-model="DDNSForm.DNS.CallAPINetwork" class="m-2" placeholder="请选择">
<el-option v-for="item in TCPNetworkTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</div>
<p>DNS接口调用代理设置</p>
<div class="fromitemChildDivRadius">
@ -991,7 +1004,7 @@
<span class="dialog-footer">
<el-button @click="addDDNSDialogVisible = false">取消</el-button>
<el-button type="primary" @click="exeAddOrAlterDDNSOption">{{ DDNSFormOptionType == "add" ? '添加' :
'修改'
'修改'
}}
</el-button>
</span>
@ -1159,6 +1172,21 @@ const DNSCallbackServerList = [
},
]
const TCPNetworkTypeList = [
{
value: '',
label: 'tcp',
},
{
value: 'tcp4',
label: 'tcp4',
},
{
value: 'tcp6',
label: 'tcp6',
},
]
const WebHookTest = () => {
console.log("WebHookTest")
@ -1584,6 +1612,7 @@ const DDNSForm = ref(
Secret: "",
ForceInterval: 3600,
ResolverDoaminCheck: false,
CallAPINetwork: "",
DNSServerList: [""],
HttpClientProxyType: "",
HttpClientProxyAddr: "",
@ -1634,6 +1663,7 @@ const preDDNSFrom = ref(
ResolverDoaminCheck: false,
DNSServerList: [''],
HttpClientProxyType: "",
CallAPINetwork: "",
HttpClientProxyAddr: "",
HttpClientProxyUser: "",
HttpClientProxyPassword: "",
@ -1701,6 +1731,7 @@ const showAddOrAlterDDNSTaskDialog = (optionType: string, task: any) => {
ResolverDoaminCheck: true,
DNSServerList: [],
HttpClientProxyType: "",
CallAPINetwork: "",
HttpClientProxyAddr: "",
HttpClientProxyUser: "",
HttpClientProxyPassword: "",
@ -1755,6 +1786,7 @@ const showAddOrAlterDDNSTaskDialog = (optionType: string, task: any) => {
ResolverDoaminCheck: true,
DNSServerList: [],
HttpClientProxyType: "",
CallAPINetwork: "",
HttpClientProxyAddr: "",
HttpClientProxyUser: "",
HttpClientProxyPassword: "",
@ -1800,6 +1832,7 @@ const showAddOrAlterDDNSTaskDialog = (optionType: string, task: any) => {
ResolverDoaminCheck: task.DNS.ResolverDoaminCheck,
DNSServerList: task.DNS.DNSServerList,
HttpClientProxyType: task.DNS.HttpClientProxyType,
CallAPINetwork: task.DNS.CallAPINetwork,
HttpClientProxyAddr: task.DNS.HttpClientProxyAddr,
HttpClientProxyUser: task.DNS.HttpClientProxyUser,
HttpClientProxyPassword: task.DNS.HttpClientProxyPassword,
@ -1852,6 +1885,7 @@ const showAddOrAlterDDNSTaskDialog = (optionType: string, task: any) => {
ResolverDoaminCheck: task.DNS.ResolverDoaminCheck,
DNSServerList: task.DNS.DNSServerList,
HttpClientProxyType: task.DNS.HttpClientProxyType,
CallAPINetwork: task.DNS.CallAPINetwork,
HttpClientProxyAddr: task.DNS.HttpClientProxyAddr,
HttpClientProxyUser: task.DNS.HttpClientProxyUser,
HttpClientProxyPassword: task.DNS.HttpClientProxyPassword,
@ -2495,7 +2529,7 @@ onUnmounted(() => {
margin-left: 3px;
margin-top: 3px;
margin-right: 3px;
margin-bottom: 25px;
margin-bottom: 5px;
min-width: 1200px;
}

View File

@ -93,7 +93,7 @@ const Login = () => {
if (res.ret == 0) {
MessageShow("success", "登录成功")
global.storage.setItem("token",res.token)
global.currentPage.value = "#set"
//global.currentPage.value = "#set"
location.hash="#set"
//console.log("cookies:"+res.cookies)

View File

@ -10,11 +10,14 @@
<div class="formradius" :style="{
borderRadius: 'base',
}">
<el-form :model="form" class="SetForm" label-width="auto">
<el-form-item label="后台管理端口" id="adminListen">
<el-input-number v-model="form.AdminWebListenPort" autocomplete="off" />
</el-form-item>
<div class="AdminListenDivRadius">
<p>后台管理入口设置</p>
<el-form-item label="外网访问" id="adminListen">
<el-switch v-model="form.AllowInternetaccess" class="mb-1" inline-prompt
@ -22,6 +25,30 @@
active-text="允许" inactive-text="禁止" />
</el-form-item>
<el-form-item label="端口(http)" id="adminListen">
<el-input-number v-model="form.AdminWebListenPort" autocomplete="off" />
</el-form-item>
<el-form-item label="TLS端口(https)" id="adminListen">
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
外网访问时建议使用https端口访问<br />
自行确认启用前先添加对应域名的SSL证书<br />
保存修改或增删SSL证书后请手动重启程序使得设置或证书生效<br />
</template>
<el-switch v-model="form.AdminWebListenTLS" class="mb-1" inline-prompt
width="50px"
active-text="启用" inactive-text="禁用" />
</el-tooltip>
</el-form-item>
<el-form-item label="端口(https)" id="adminListen" v-show="form.AdminWebListenTLS">
<el-input-number v-model="form.AdminWebListenHttpsPort" autocomplete="off" />
</el-form-item>
</div>
<div class="AdminListenDivRadius">
<el-form-item label="管理登录账号" id="adminAccount">
<el-input v-model="form.AdminAccount" placeholder="管理登录账号" autocomplete="off"
style="witdh:390px;" />
@ -30,19 +57,24 @@
<el-form-item label="管理登录密码" id="adminPassword">
<el-input v-model="form.AdminPassword" placeholder="管理登录密码" autocomplete="off" />
</el-form-item>
</div>
<div class="AdminListenDivRadius">
<el-form-item label="日志记录最大条数" id="logMaxSize">
<el-input-number v-model="form.LogMaxSize" autocomplete="off" :min="1024" :max="40960" />
</el-form-item>
</div>
<el-form-item label="全局最大端口代理数量" id="proxyCountLimit">
<!-- <el-form-item label="全局最大端口代理数量" id="proxyCountLimit">
<el-input-number v-model="form.ProxyCountLimit" autocomplete="off" :min="1" :max="1024" />
</el-form-item>
<el-form-item label="全局最大连接数" id="globalMaxConnections">
<el-input-number v-model="form.GlobalMaxConnections" autocomplete="off" :min="1" :max="65535" />
</el-form-item>
</el-form-item> -->
@ -115,7 +147,7 @@ const callRestoreConfigureAPI = (res: any, uploadFile: any, uploadFiles: any) =>
let fileName = res.file
ElMessageBox.confirm(
"确认要将[" + fileName + "]替换为大吉现有配置?替换完成后大吉会自动重启",
"确认要将[" + fileName + "]替换为Lucky现有配置?替换完成后Lucky会自动重启",
'Warning',
{
confirmButtonText: '确认',
@ -126,11 +158,11 @@ const callRestoreConfigureAPI = (res: any, uploadFile: any, uploadFiles: any) =>
.then(() => {
apiGetRestoreConfigureConfirm(res.restoreConfigureKey).then(res => {
if (res.ret != 0) {
MessageShow("error", "将[" + fileName + "]替换为大吉现有配置出错:" + res.msg)
MessageShow("error", "将[" + fileName + "]替换为Lucky现有配置出错:" + res.msg)
return
}
MessageShow("success", "将[" + fileName + "]替换为大吉现有配置成功")
MessageShow("success", "将[" + fileName + "]替换为Lucky现有配置成功")
setTimeout(() => {
window.location.href = window.location.protocol + "//" + window.location.hostname + ":" + res.port;
@ -138,7 +170,7 @@ const callRestoreConfigureAPI = (res: any, uploadFile: any, uploadFiles: any) =>
}).catch((error) => {
console.log("网络出错:" + error)
MessageShow("error", "将[" + res.file + "]替换为大吉现有配置出错:" + error)
MessageShow("error", "将[" + res.file + "]替换为Lucky现有配置出错:" + error)
})
})
@ -149,10 +181,12 @@ const callRestoreConfigureAPI = (res: any, uploadFile: any, uploadFiles: any) =>
const rawData = {
AdminWebListenPort: 1,
AdminWebListenTLS:false,
AdminWebListenHttpsPort:16626,
AdminAccount: "",
AdminPassword: "",
ProxyCountLimit: 1,
GlobalMaxConnections: 1,
// ProxyCountLimit: 1,
// GlobalMaxConnections: 1,
AllowInternetaccess: false,
LogMaxSize:1024,
}
@ -189,8 +223,8 @@ const resetFormData = () => {
form.value.AdminWebListenPort = preFormData.value.AdminWebListenPort
form.value.AdminAccount = preFormData.value.AdminAccount
form.value.AdminPassword = preFormData.value.AdminPassword
form.value.ProxyCountLimit = preFormData.value.ProxyCountLimit
form.value.GlobalMaxConnections = preFormData.value.GlobalMaxConnections
//form.value.ProxyCountLimit = preFormData.value.ProxyCountLimit
//form.value.GlobalMaxConnections = preFormData.value.GlobalMaxConnections
form.value.AllowInternetaccess = preFormData.value.AllowInternetaccess
}
@ -198,8 +232,8 @@ const syncToPreFormData = (data: any) => {
preFormData.value.AdminWebListenPort = data.value.AdminWebListenPort
preFormData.value.AdminAccount = data.value.AdminAccount
preFormData.value.AdminPassword = data.value.AdminPassword
preFormData.value.ProxyCountLimit = data.value.ProxyCountLimit
preFormData.value.GlobalMaxConnections = data.value.GlobalMaxConnections
// preFormData.value.ProxyCountLimit = data.value.ProxyCountLimit
// preFormData.value.GlobalMaxConnections = data.value.GlobalMaxConnections
preFormData.value.AllowInternetaccess = data.value.AllowInternetaccess
}
@ -280,6 +314,20 @@ onMounted(() => {
<style scoped>
.AdminListenDivRadius {
border: 2px solid var(--el-border-color);
border-radius: 10px;
margin-left: 3px;
margin-top: 15px;
margin-right: 3px;
margin-bottom: 15px;
width: 456;
padding-top: 9px;
padding-left: 9px;
padding-right: 9px;
}
.SetForm {
margin-top: 15px;
margin-left: 20px;

View File

@ -34,18 +34,33 @@
<span>端口转发</span>
</template>
<el-menu-item index="#relayset">
<!-- <el-menu-item index="#relayset">
<el-icon>
<List />
</el-icon>
<template #title>转发规则</template>
</el-menu-item> -->
<el-menu-item index="#portforward">
<el-icon>
<List />
</el-icon>
<template #title>转发规则列表</template>
</el-menu-item>
<el-menu-item index="#portforwardset">
<el-icon>
<List />
</el-icon>
<template #title>设置</template>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="#reverseproxy">
<template #title>
<el-icon>
<Position />
<Connection />
</el-icon>
<span>反向代理</span>
</template>
@ -56,6 +71,7 @@
</el-icon>
<template #title>反向代理规则列表</template>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="#ddns">
@ -81,10 +97,47 @@
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="#wol">
<template #title>
<el-icon>
<Bell />
</el-icon>
<span>网络唤醒</span>
</template>
<el-menu-item index="#wol">
<el-icon>
<Bell />
</el-icon>
<template #title>网络唤醒设备列表</template>
</el-menu-item>
<el-menu-item index="#wolset">
<el-icon>
<setting />
</el-icon>
<template #title>网络唤醒设置</template>
</el-menu-item>
</el-sub-menu>
<el-divider style="margin-top: 0px;margin-bottom: 0px;" />
<el-sub-menu index="#safe">
<template #title>
<el-icon>
<Guide />
</el-icon>
<span>安全相关</span>
</template>
<el-sub-menu index="#safe">
<template #title>
<el-icon>
<Guide />
@ -114,6 +167,22 @@
</el-menu-item>
</el-sub-menu>
<el-menu-item index="#ssl">
<el-icon>
<Lock />
</el-icon>
<template #title>SSL证书</template>
</el-menu-item>
</el-sub-menu>
<el-menu-item index="#set">

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,220 @@
<template>
<div class="PageRadius" :style="{
borderRadius: 'base',
}" v-loading="logLoading" element-loading-background="transparent">
<el-scrollbar height="100%">
<div class="formradius" :style="{
borderRadius: 'base',
}">
<el-form :model="form" class="SetForm" label-width="auto">
<!-- <el-tooltip content="如果不需要DDNS动态域名服务请不要打开这个开关" placement="top">
<el-form-item label="动态域名服务开关" id="adminListen">
<el-switch v-model="form.Enable" class="mb-1" inline-prompt
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" width="50px"
active-text="开启" inactive-text="停用" />
</el-form-item>
</el-tooltip>
<el-tooltip content="多数嵌入式设备启用这个开关会导致https访问失败" placement="top">
<el-form-item label="Http(s) 客户端 安全证书验证" id="adminListen">
<el-switch v-model="form.HttpClientSecureVerify" class="mb-1" inline-prompt
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" width="50px"
active-text="启用" inactive-text="禁用" />
</el-form-item> -->
<!-- </el-tooltip> -->
<div class="AdminListenDivRadius">
<el-tooltip content="同一端口tcp和udp类型各算一个,(0-1024)" placement="top">
<el-form-item label="全局端口转发最大数量" label-width="auto" min="0" max="1024">
<el-input-number v-model="form.PortForwardsLimit" autocomplete="off" />
</el-form-item>
</el-tooltip>
<el-tooltip content="端口转发全局TCP最大并发连接数,(0-4096)" placement="top">
<el-form-item label="端口转发全局TCP最大并发连接数" label-width="auto" min="0" max="4096">
<el-input-number v-model="form.TCPPortforwardMaxConnections" autocomplete="off" />
</el-form-item>
</el-tooltip>
<el-tooltip content="端口转发全局UDP读取目标地址数据协程数限制,(0-4096)" placement="top">
<el-form-item label="端口转发全局UDP读取目标地址数据协程数限制" label-width="auto" min="0" max="4096">
<el-input-number v-model="form.UDPReadTargetDataMaxgoroutineCount" autocomplete="off" />
</el-form-item>
</el-tooltip>
</div>
<!-- <el-tooltip content="DDNS任务每次执行的时间间隔,最小30秒,最长3600秒" placement="top">
<el-form-item label="时间间隔(秒)" label-width="auto" :min="30" :max="3600">
<el-input-number v-model="form.Intervals" autocomplete="off" />
</el-form-item>
</el-tooltip> -->
</el-form>
<el-button type="primary" round @click="RequestAlterPortForwardConfigure">保存修改</el-button>
</div>
</el-scrollbar>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, computed, reactive } from 'vue'
import { apiQueryPortForwardConfigure, apiAlterPortForwardConfigure } from '../apis/utils'
import { MessageShow } from '../utils/ui'
const logLoading = ref(true)
const rawData = {
// Enable: false,
// HttpClientSecureVerify: false,
// Intervals: 0,
PortForwardsLimit: 0,
TCPPortforwardMaxConnections:0,
UDPReadTargetDataMaxgoroutineCount:0,
}
const form = ref(rawData)
const preFormData = ref(rawData)
const resetFormData = () => {
form.value.PortForwardsLimit = preFormData.value.PortForwardsLimit
form.value.TCPPortforwardMaxConnections = preFormData.value.TCPPortforwardMaxConnections
}
const syncToPreFormData = (data: any) => {
preFormData.value.PortForwardsLimit = data.value.PortForwardsLimit
preFormData.value.TCPPortforwardMaxConnections = data.TCPPortforwardMaxConnections
}
const queryPortForwardsConfigure = () => {
apiQueryPortForwardConfigure().then((res) => {
if (res.ret == 0) {
logLoading.value = false
form.value = res.configure
syncToPreFormData(form)
return
}
MessageShow("error", "获取端口转发配置出错")
}).catch((error) => {
MessageShow("error", "获取端口转发配置出错")
})
}
const RequestAlterPortForwardConfigure = () => {
apiAlterPortForwardConfigure(form.value).then((res) => {
if (res.ret == 0) {
MessageShow("success", "配置修改成功")
queryPortForwardsConfigure()
return
}
resetFormData()
MessageShow("error", res.msg)
}).catch((error) => {
console.log("配置修改失败,网络请求出错:" + error)
MessageShow("error", "配置修改失败,网络请求出错")
resetFormData()
})
}
onMounted(() => {
queryPortForwardsConfigure()
})
</script>
<style scoped>
.AdminListenDivRadius {
border: 2px solid var(--el-border-color);
border-radius: 10px;
margin-left: 3px;
margin-top: 15px;
margin-right: 3px;
margin-bottom: 15px;
width: 456;
padding-top: 9px;
padding-left: 9px;
padding-right: 9px;
}
.SetForm {
margin-top: 15px;
margin-left: 20px;
}
.formradius {
border: 0px solid var(--el-border-color);
border-radius: 0;
margin: 0 auto;
width: fit-content;
padding: 15px;
}
#adminListen {
width: 360px;
}
#adminAccount {
width: 30vw;
max-width: 360px;
min-width: 300px;
}
#adminPassword {
width: 30vw;
max-width: 360px;
min-width: 300px;
}
#proxyCountLimit {
width: 360px;
}
#globalMaxConnections {
width: 360px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,11 @@
<el-button color="#409eff" size="small" v-show="true">
{{ rule.ListenPort }}
</el-button>
<el-button :type="rule.EnableTLS!=true?'info':'primary'" size="small" v-show="true">
{{ rule.EnableTLS==true?'TLS已启用':'TLS未启用' }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="规则操作" :span="2">
@ -96,7 +101,7 @@
<el-button color="#409eff" size="small" v-show="true">
{{
(rule.DefaultProxy.Locations==undefined||rule.DefaultProxy.Locations==null)||rule.DefaultProxy.Locations.length
<=0?'未设置默认规则后端地址':rule.DefaultProxy.Locations.length==1?rule.DefaultProxy.Locations[0]:rule.DefaultProxy.Locations[0]+'...等多个'}}
<=0?'未设置':rule.DefaultProxy.Locations.length==1?rule.DefaultProxy.Locations[0]:rule.DefaultProxy.Locations[0]+'...'}}
</el-button>
</el-tooltip>
</el-descriptions-item>
@ -140,7 +145,8 @@
<template #content v-if="!rule.DefaultProxy.EnableBasicAuth">
Basic认证未启用<br />
</template>
<el-button color="#6666ff" size="small" v-show="true">
<el-button color="#6666ff" size="small"
v-show="true" :disabled="rule.DefaultProxy.EnableBasicAuth == true ? false : true">
{{ rule.DefaultProxy.EnableBasicAuth==false?'Basic认证未启用':'Basic认证已启用' }}
</el-button>
</el-tooltip>
@ -186,7 +192,7 @@
<span v-html="StrArrayListToBrHtml(proxy.Domains)"></span>
</template>
<el-button color="#409eff" size="small" v-show="true">
{{ proxy.Domains.length==1?proxy.Domains[0]:proxy.Domains[0]+' ...等多个' }}
{{ proxy.Domains.length==1?proxy.Domains[0]:proxy.Domains[0]+' ...' }}
</el-button>
</el-tooltip>
</el-descriptions-item>
@ -197,7 +203,7 @@
<span v-html="StrArrayListToBrHtml(proxy.Locations)"></span>
</template>
<el-button color="#409eff" size="small" v-show="true">
{{ proxy.Locations.length==1?proxy.Locations[0]:proxy.Locations[0]+' ...等多个' }}
{{ proxy.Locations.length==1?proxy.Locations[0]:proxy.Locations[0]+' ...' }}
</el-button>
</el-tooltip>
</el-descriptions-item>
@ -227,7 +233,7 @@
<template #content v-if="!proxy.EnableBasicAuth">
Basic认证未启用<br />
</template>
<el-button color="#6666ff" size="small" v-show="true">
<el-button color="#6666ff" size="small" v-show="true" :disabled="proxy.EnableBasicAuth == true ? false : true">
{{ proxy.EnableBasicAuth==false?'Basic认证未启用':'Basic认证已启用' }}
</el-button>
</el-tooltip>
@ -306,10 +312,16 @@
<el-input-number v-model="ruleForm.ListenPort" autocomplete="off" />
</el-form-item>
<el-form-item label="TLS" label-width="auto" v-if="false">
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
启用前请先添加SSL证书<br/>
增加删除证书后需要重启规则新证书才生效<br/>
</template>
<el-form-item label="TLS" label-width="auto" v-if="true">
<el-switch v-model="ruleForm.EnableTLS" inline-prompt width="50px" active-text="启用"
inactive-text="禁用" />
</el-form-item>
</el-tooltip>
@ -976,11 +988,6 @@
</el-scrollbar>
<!-- <p v-for="log in reverseProxyLogsDialogData">
{{log.LogTime}} &nbsp; &nbsp; &nbsp; {{log.LogContent}}
</p> -->
<el-pagination :page-size=reverseProxyLogsPageSize :page-sizes="[10,20,50, 100, 200, 300,400,500]" :small="false"
:disabled="false" :background="false" layout="total, sizes, prev, pager, next, jumper"
:current-page="reverseProxyLogsDialogCurrentPage" :total=reverseProxyLogsTotal
@ -1803,7 +1810,7 @@ onUnmounted(() => {
margin-left: 3px;
margin-top: 3px;
margin-right: 3px;
margin-bottom: 25px;
margin-bottom: 10px;
min-width: 1350px;
}

View File

@ -0,0 +1,445 @@
<template>
<div class="PageRadius" :style="{
borderRadius: 'base',
}">
<!-- <el-affix position="bottom" :offset="0" class="affix-container">
<el-button type="primary" @click="showAddSSLDialog">SSL证书添加 <el-icon>
<Plus />
</el-icon>
</el-button>
</el-affix> -->
<el-scrollbar height="100%">
<div class="itemradius" :style="{
borderRadius: 'base',
}" v-for="ssl in SSLList">
<el-descriptions :column="6" border>
<el-descriptions-item label="证书备注" :span="2">
<el-button size="small" v-show="true">
{{ ssl.Remark == '' ? '未备注' : ssl.Remark }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="添加时间" :span="2">
<el-button size="small" v-show="true">
{{ ssl.AddTime }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="操作" :span="2">
<el-tooltip :content="ssl.Enable == true ? '证书已启用' : '证书已禁用'" placement="top">
<el-switch v-model="ssl.Enable" inline-prompt active-text="开" inactive-text="关"
:before-change="sslEnableClick.bind(this, ssl.Enable, ssl)" size="large" />
</el-tooltip>
&nbsp;&nbsp;
<el-button size="small" type="primary" @click="showAlterRemarkDialog(ssl)">修改备注</el-button>
<el-button size="small" type="danger" @click="deleteSSL(ssl)">删除</el-button>
</el-descriptions-item>
<div v-for="cert in ssl.CertsInfo">
<!-- <el-descriptions :column="6" border> -->
<el-descriptions-item label="绑定域名" :span="2">
<el-tooltip placement="bottom" effect="dark" :trigger-keys="[]" content="">
<template #content>
<span v-html="StrArrayListToBrHtml(cert.Domains)"></span>
</template>
<el-button size="small" v-show="true" type="primary">
{{cert.Domains.length==1?cert.Domains[0]:cert.Domains[0]+'...'}}
</el-button>
</el-tooltip>
</el-descriptions-item>
<el-descriptions-item label="颁发时间" :span="2">
<el-button size="small" v-show="true" type="info">
{{ cert.NotBeforeTime }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="到期时间" :span="2">
<el-button size="small" v-show="true" type="warning">
{{ cert.NotAfterTime }}
</el-button>
</el-descriptions-item>
<!-- </el-descriptions> -->
</div>
</el-descriptions>
</div>
</el-scrollbar>
<el-affix position="bottom" :offset="30" class="affix-container">
<el-button type="primary" :round=true @click="showAddSSLDialog">SSL证书添加
<el-icon class="el-icon--right">
<Plus />
</el-icon>
</el-button>
</el-affix>
<el-dialog v-if="addSSLDialogVisible" v-model="addSSLDialogVisible" title="添加SSL证书" draggable
:show-close="false" :close-on-click-modal="false" width="400px">
<el-form :model="addSSLForm">
<el-form-item label="备注" label-width="auto">
<el-input v-model="addSSLForm.Remark" autocomplete="off" />
</el-form-item>
<el-form-item label="证书" label-width="auto">
<el-upload class="inline-block" :multiple="true" :action="getFileBase64API()"
:before-upload="beforeUpload" :show-file-list="false" :headers="{ 'Authorization': GetToken() }"
:on-success="callbackGetCreFileBase64">
<el-button round class='margin-change'>{{uploadCreButtontext}}</el-button>
</el-upload>
</el-form-item>
<el-form-item label="Key" label-width="auto">
<el-upload class="inline-block" :multiple="true" :action="getFileBase64API()"
:before-upload="beforeUpload" :show-file-list="false" :headers="{ 'Authorization': GetToken() }"
:on-success="callbackGetKeyFileBase64">
<el-button round class='margin-change'>{{uploadKeyButtontext}}</el-button>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="addSSLDialogVisible = false">取消</el-button>
<el-button type="primary" @click="addSSL">添加</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-if="alterRemarkDialogShow" v-model="alterRemarkDialogShow" :title=alterRemarkDialogSSLText
draggable :show-close="false" :close-on-click-modal="false" width="400px">
<el-form-item label="备注" label-width="auto">
<el-input v-model="alterRemarkDialogValue" autocomplete="off" />
</el-form-item>
<template #footer>
<span class="dialog-footer">
<el-button @click="alterRemarkDialogShow = false">取消</el-button>
<el-button type="primary" @click="alterSSLRemark">修改</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue'
import { ElMessageBox } from 'element-plus'
import { MessageShow } from '../utils/ui'
import { StrArrayListToBrHtml } from '../utils/utils'
import { GetToken, apiAddSSL, apiGetSSLList, apiDeleteSSL, apiAlterSSL } from '../apis/utils'
import type { UploadProps } from 'element-plus'
var SSLList = ref([
{
Key: "",
Remark: "",
Enable: true,
AddTime: "",
CertsInfo: [{
Domains: [''],
NotBeforeTime: '',
NotAfterTime: ''
},]
}
])
const addSSLDialogVisible = ref(false)
const addSSLForm = ref({ Remark: "", CertBase64: "", KeyBase64: "" })
const uploadCreButtontext = ref("")
const uploadKeyButtontext = ref("")
const getFileBase64API = () => {
var baseURL = "/" //
if (process.env.NODE_ENV == "development") {
//开发环境下这个改为自己的接口地址
baseURL = 'http://192.168.31.70:16601/'
}
return baseURL + "api/getfilebase64"
}
const alterRemarkDialogShow = ref(false)
const alterRemarkDialogValue = ref("")
const alterRemarkDialogSSLText = ref("")
const alterRemarkDialogSSLKey = ref("")
const showAlterRemarkDialog = (ssl) => {
alterRemarkDialogShow.value = true
alterRemarkDialogSSLKey.value = ssl.Key
alterRemarkDialogValue.value = ssl.Remark
alterRemarkDialogSSLText.value = ssl.CertsInfo[0].Domains[0];
}
const callbackGetCreFileBase64 = (res: any, uploadFile: any, uploadFiles: any) => {
if (res.ret != 0) {
MessageShow("error", res.msg)
return
}
console.log("file:" + res.file)
uploadCreButtontext.value = res.file
//console.log("base64:"+res.base64)
addSSLForm.value.CertBase64 = res.base64
}
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
if (rawFile.size / 1024 / 1024 > 1) {
MessageShow("error",'文件不能大于1M')
return false
}
return true
}
const callbackGetKeyFileBase64 = (res: any, uploadFile: any, uploadFiles: any) => {
if (res.ret != 0) {
MessageShow("error", res.msg)
return
}
console.log("file:" + res.file)
uploadKeyButtontext.value = res.file
//console.log("base64:"+res.base64)
addSSLForm.value.KeyBase64 = res.base64
}
const showAddSSLDialog = () => {
addSSLDialogVisible.value = true
addSSLForm.value.CertBase64 = ""
addSSLForm.value.KeyBase64 = ""
uploadCreButtontext.value = "选择要上传的证书文件"
uploadKeyButtontext.value = "选择要上传的Key文件"
}
const sslEnableClick = (enable, ssl) => {
const enableText = enable == false ? "启用" : "禁用";
const sslText = ssl.Remark != "" ? ssl.Remark : ssl.CertsInfo[0].Domains[0];
const sslName = "[" + sslText + "]"
return new Promise((resolve, reject) => {
ElMessageBox.confirm(
'确认要' + enableText + " 证书 " + sslName + "?",
'Warning',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
apiAlterSSL(ssl.Key, "enable", !enable).then(res => {
if (res.ret == 0) {
resolve(true)
MessageShow("success", "证书 " + sslName + enableText + "成功")
return
}
resolve(false)
MessageShow("error", "证书 " + sslName + enableText + "失败: " + res.msg)
// if (res.syncres != undefined && res.syncres != "") {
// Notification("warn", res.syncres, 0)
// }
}).catch((error) => {
resolve(false)
console.log("证书 " + sslName + enableText + "失败" + ":请求出错" + error)
MessageShow("error", "证书 " + sslName + enableText + "失败" + ":请求出错")
})
})
.catch(() => {
resolve(false)
})
})
}
const alterSSLRemark = () => {
apiAlterSSL(alterRemarkDialogSSLKey.value, "remark", alterRemarkDialogValue.value).then(res => {
if (res.ret == 0) {
alterRemarkDialogShow.value = false
MessageShow("success", "证书 " + alterRemarkDialogSSLText.value + " 备注修改成功")
querySSLList()
return
}
MessageShow("error", "证书 " + alterRemarkDialogSSLText.value + " 备注修改失败: " + res.msg)
}).catch((error) => {
console.log("证书 " + alterRemarkDialogSSLText.value + " 备注修改失败" + ":请求出错" + error)
MessageShow("error", "证书 " + alterRemarkDialogSSLText.value + " 备注修改失败" + ":请求出错")
})
}
const addSSL = () => {
if (addSSLForm.value.CertBase64 == "") {
MessageShow("error", "请选择要保存的证书文件")
return
}
if (addSSLForm.value.KeyBase64 == "") {
MessageShow("error", "请选择要保存的Key文件")
return
}
apiAddSSL(addSSLForm.value).then((res) => {
if (res.ret == 0) {
//let item = { IP: addWhiteListForm.value.IP, Effectivetime: res.data }
//whitelist.value.push(item)
addSSLDialogVisible.value = false
MessageShow("success", "添加证书成功")
querySSLList()
return
}
MessageShow("error", res.msg)
}).catch((error) => {
console.log("添加SSL证书出错 " + error)
MessageShow("error", "添加SSL证书出错 " + error)
})
}
const deleteSSL = (ssl) => {
const sslText = ssl.Remark != "" ? ssl.Remark : ssl.CertsInfo[0].Domains[0];
ElMessageBox.confirm(
'确认要删除 ' + sslText + " 的证书?",
'Warning',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
apiDeleteSSL(ssl.Key).then((res) => {
if (res.ret == 0) {
MessageShow("success", "证书删除成功")
querySSLList()
return
}
MessageShow("error", res.msg)
}).catch((error) => {
console.log("证书删除失败,网络请求出错:" + error)
MessageShow("error", "证书删除失败,网络请求出错")
})
})
}
const querySSLList = () => {
apiGetSSLList().then((res) => {
if (res.ret == 0) {
console.log(res.list)
if (res.list != null) {
SSLList.value = res.list
} else {
// whitelist.value= []
SSLList.value = []
}
return
}
MessageShow("error", res.msg)
//console.log("getAdminURL "+getAdminURL())
}).catch((error) => {
MessageShow("error", "查询证书列表列表出错")
})
}
const keydown = (e) => {
if (e.keyCode != 13) {
return
}
if (!addSSLDialogVisible.value) {
return
}
addSSL()
}
onMounted(() => {
querySSLList();
window.addEventListener('keydown', keydown)
})
</script>
<style scoped>
.formradius {
border: 0px solid var(--el-border-color);
border-radius: 0;
margin: 0 auto;
width: fit-content;
padding: 15px;
}
.itemradius {
border: 1px solid var(--el-border-color);
border-radius: 0;
margin-left: 3px;
margin-top: 3px;
margin-right: 3px;
margin-bottom: 25px;
min-width: 1350px;
}
.affix-container {
text-align: center;
border-radius: 4px;
width: 3vw;
background: var(--el-color-primary-light-9);
}
</style>

View File

@ -4,8 +4,9 @@
<p class="status">CPU全局使用率:{{ status.usedCPU }}</p>
<p class="status">当前进程CPU使用率:{{ status.currentProcessUsedCPU }}</p>
<p class="status">进程协程数:{{ status.goroutine }} 占用内存:{{ status.processUsedMem }}</p>
<p class="status">Lucky 全局连接数:{{ status.currentConnections }} </p>
<p class="status">Lucky 全局限制连接数:{{ status.maxConnections }}</p>
<p class="status">端口转发TCP总连接数:{{ status.currentTCPConnections }} </p>
<p class="status">端口转发全局UDP读取目标地址数据协程数:{{ status.currentUDPConnections }} </p>
<p class="status">端口转发TCP全局限制连接数:{{ status.maxTCPConnections }}</p>
<p class="status">Lucky 启动时间:{{ status.runTime }}</p>
@ -62,8 +63,9 @@ var status=ref({totleMem: '0m',
currentProcessUsedCPU:"0%",
goroutine:"0",
processUsedMem:"0m",
currentConnections:0,
maxConnections:0,
currentTCPConnections:0,
currentUDPConnections:0,
maxTCPConnections:0,
proxysStatus:"",
runTime:""})

View File

@ -0,0 +1,400 @@
<template>
<div class="PageRadius" :style="{
borderRadius: 'base',
}">
<el-scrollbar height="100%">
<div class="itemradius" :style="{
borderRadius: 'base',
}" v-for="device in deviceList" >
<el-descriptions :column="4" border >
<el-descriptions-item label="设备操作">
<el-tooltip placement="bottom" effect="dark" :trigger-keys="[]" content="">
<template #content>
唤醒<br />
</template>
<el-button size="small" :icon="Bell" circle type="success" @click="wakeup(device)">
</el-button>
</el-tooltip>
<el-tooltip placement="bottom" effect="dark" :trigger-keys="[]" content="">
<template #content>
关机<br />
</template>
<el-button size="small" :icon="SwitchButton" circle type="danger">
</el-button>
</el-tooltip>
&nbsp; &nbsp;
<el-button size="small" type="primary" @click="showAlterDeviceDialog(device)">
编辑
</el-button>
<el-button size="small" type="danger" @click="deleteDevice(device)">
删除
</el-button>
</el-descriptions-item>
<el-descriptions-item label="设备名称" >
<el-button size="default" v-show="true">
{{ device.DeviceName == '' ? '未命名设备' : device.DeviceName }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="设备MAC">
<el-tooltip placement="bottom" effect="dark" :trigger-keys="[]" content="">
<template #content>
<span v-html="StrArrayListToBrHtml(device.MacList)"></span>
</template>
<el-button size="small" v-show="true">
{{device.MacList.length==1?device.MacList[0]:device.MacList[0]+'...' }}
</el-button>
</el-tooltip>
</el-descriptions-item>
<el-descriptions-item label="魔法包广播地址">
<el-tooltip placement="bottom" effect="dark" :trigger-keys="[]" content="">
<template #content>
<span v-html="StrArrayListToBrHtml(device.BroadcastIPs)"></span>
</template>
<el-button size="small" v-show="true">
{{device.BroadcastIPs.length==1?device.BroadcastIPs[0]:device.BroadcastIPs[0]+'...' }}
</el-button>
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
端口<br />
</template>
<el-button size="small" v-show="true">
{{device.Port}}
</el-button>
</el-tooltip>
</el-descriptions-item>
</el-descriptions>
</div>
</el-scrollbar>
<el-affix position="bottom" :offset="30" class="affix-container">
<el-button type="primary" :round=true @click="showAddDeviceDialog">添加可唤醒的设备
<el-icon class="el-icon--right">
<Plus />
</el-icon>
</el-button>
</el-affix>
<el-dialog v-if="deviceDialogShow" v-model="deviceDialogShow" :title=deviceDialogTitle draggable
:show-close="false" :close-on-click-modal="false" width="400px">
<el-form-item label="设备名称" label-width="120px">
<el-input v-model="deviceForm.DeviceName" autocomplete="off" />
</el-form-item>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
每行填写一个MAC<br />
一般情况填写一个MAC地址即可<br />
</template>
<el-form-item label-width="120px" label="设备MAC">
<el-input v-model="deviceFormMacListArea" :autosize="{ minRows: 1, maxRows: 3 }" placeholder=""
type="textarea" wrap="off">
</el-input>
</el-form-item>
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
不建议使用255.255.255.255<br />
一般家用情况,如果路由器的管理地址是192.168.31.1,则填写192.168.31.255作为广播地址<br />
菜鸟没法确定的话可留空,程序会遍历全部IPV4地址发送广播<br />
每行填写一个广播地址<br />
一般情况填写一个广播地址即可<br />
</template>
<el-form-item label-width="120px" label="魔方包广播地址">
<el-input v-model="deviceFormBroadcastIPsArea" :autosize="{ minRows: 1, maxRows: 3 }" placeholder=""
type="textarea" wrap="off">
</el-input>
</el-form-item>
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
默认端口为9<br />
没特殊情况不要修改<br />
</template>
<el-form-item label="端口" label-width="120px" :min="1" :max="65535">
<el-input-number v-model="deviceForm.Port" autocomplete="off" />
</el-form-item>
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
每次执行唤醒时重复发送魔方包的次数<br />
可设置范围(1-10)<br />
</template>
<el-form-item label="重复次数" label-width="120px" :min="1" :max="10">
<el-input-number v-model="deviceForm.Repeat" autocomplete="off" />
</el-form-item>
</el-tooltip>
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
<template #content>
一般情况忽略即可<br />
目标唤醒设备和lucky不在同一局域网时才用得上这个开关<br />
发送广播的同时交给中继设备发送<br />
</template>
<el-form-item label="转播开关" label-width="120px" v-if="true">
<el-switch v-model="deviceForm.Relay" inline-prompt width="50px" active-text="启用"
inactive-text="禁用" />
</el-form-item>
</el-tooltip>
<template #footer>
<span class="dialog-footer">
<el-button @click="deviceDialogShow = false">取消</el-button>
<el-button type="primary" @click="addOreAlterDevice">{{deviceDialogCommitButtonText}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue'
import { ElMessageBox } from 'element-plus'
import { MessageShow } from '../../utils/ui'
import { StrArrayListToBrHtml, StrArrayListToArea, StringToArrayList } from '../../utils/utils'
import { GetToken, apiGetWOLDeviceList, apiAddWOLDevice, apiDeleteWOLDevice, apiAlterWOLDevice,apiWOLDeviceWakeUp } from '../../apis/utils'
import {
SwitchButton,
AlarmClock,
Bell,
} from '@element-plus/icons-vue'
const deviceDialogShow = ref(false)
const deviceDialogTitle = ref("")
const deviceDialogCommitButtonText = ref("")
const deviceFormMacListArea = ref("")
const deviceFormBroadcastIPsArea = ref("")
const deviceFormActionType = ref("")
const deviceList = ref([{
Key: "",
DeviceName: "",
MacList: [''],
BroadcastIPs: [''],
Port: 9,
Relay: true,
Repeat: 5,
},])
const deviceForm = ref({
Key: "",
DeviceName: "",
MacList: [''],
BroadcastIPs: [''],
Port: 9,
Relay: true,
Repeat: 5,
})
const deleteDevice = (device)=>{
const deviceName = device.DeviceName==""?device.MacList[0]:device.DeviceName;
const deviceText = "[" + deviceName +"]"
ElMessageBox.confirm(
'确认要删除待唤醒设备 ' + deviceText + "?",
'Warning',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
console.log("确认删除 " + deviceText)
apiDeleteWOLDevice(device.Key).then((res) => {
if (res.ret == 0) {
queryDeviceList();
MessageShow("success", "删除成功")
} else {
MessageShow("error", res.msg)
}
}).catch((error) => {
console.log("删除唤醒设备失败,网络请求出错:" + error)
MessageShow("error", "删除唤醒设备失败,网络请求出错")
})
})
.catch(() => {
})
}
const wakeup = (device)=>{
apiWOLDeviceWakeUp(device.Key).then((res) => {
if (res.ret == 0) {
MessageShow("success", "唤醒指令已发送")
queryDeviceList();
return
}
MessageShow("error", res.msg)
}).catch((error) => {
console.log("唤醒指令发送失败,网络请求出错:" + error)
MessageShow("error", "唤醒指令发送失败,网络请求出错")
})
}
const addOreAlterDevice = () => {
deviceForm.value.BroadcastIPs = StringToArrayList(deviceFormBroadcastIPsArea.value)
deviceForm.value.MacList = StringToArrayList(deviceFormMacListArea.value)
switch (deviceFormActionType.value) {
case "add":
apiAddWOLDevice(deviceForm.value).then((res) => {
if (res.ret == 0) {
deviceDialogShow.value = false;
MessageShow("success", "设备添加成功")
queryDeviceList();
return
}
MessageShow("error", res.msg)
}).catch((error) => {
console.log("唤醒设备添加失败,网络请求出错:" + error)
MessageShow("error", "唤醒设备添加失败,网络请求出错")
})
break;
case "alter":
apiAlterWOLDevice(deviceForm.value).then((res) => {
if (res.ret == 0) {
deviceDialogShow.value = false;
MessageShow("success", "设备修改成功")
queryDeviceList();
return
}
MessageShow("error", res.msg)
}).catch((error) => {
console.log("唤醒设备修改失败,网络请求出错:" + error)
MessageShow("error", "唤醒设备修改失败,网络请求出错")
})
break;
default:
}
}
const showAlterDeviceDialog = (device)=>{
deviceDialogCommitButtonText.value = "修改"
deviceForm.value = {
Key: device.Key,
DeviceName: device.DeviceName,
MacList:device.MacList,
BroadcastIPs: device.BroadcastIPs,
Port: device.Port,
Relay: device.Relay,
Repeat: device.Repeat,
}
deviceFormActionType.value = "alter"
deviceFormMacListArea.value = StrArrayListToArea(device.MacList)
deviceFormBroadcastIPsArea.value = StrArrayListToArea(device.BroadcastIPs)
deviceDialogShow.value = true
}
const showAddDeviceDialog = () => {
deviceDialogCommitButtonText.value = "添加"
deviceForm.value = {
Key: "",
DeviceName: "",
MacList: [''],
BroadcastIPs: [''],
Port: 9,
Relay: true,
Repeat: 5,
}
deviceFormActionType.value = "add"
deviceFormMacListArea.value = ""
deviceFormBroadcastIPsArea.value = ""
deviceDialogShow.value = true
}
const queryDeviceList = () => {
apiGetWOLDeviceList().then((res) => {
//console.log(res.data)
deviceList.value = res.list
}).catch((error) => {
console.log("获取设备列表出错:" + error)
MessageShow("error", "获获取设备列表出错")
})
}
var timerID: any
onMounted(() => {
queryDeviceList();
timerID = setInterval(() => {
queryDeviceList();
}, 2000);
})
</script>
<style scoped>
.itemradius {
border: 1px solid var(--el-border-color);
border-radius: 0;
margin-left: 3px;
margin-top: 3px;
margin-right: 3px;
margin-bottom: 5px;
min-width: 1200px;
}
</style>

View File

@ -12,9 +12,10 @@ export function isIP(ip :string){
}
const MenuIndexList = ["#status",
"#log","#relayset","#whitelistset",
"#log","#whitelistset",
"#whitelists","#blacklists","#set",
"#login","#ddns","#ddnstasklist","#ddnsset","#about","#reverseproxylist"]
"#login","#ddns","#ddnstasklist","#ddnsset",
"#about","#reverseproxylist","#ssl","#portforward","#portforwardset","#wol"]
export function PageExist(page:string) {
for(let i in MenuIndexList){
@ -82,4 +83,13 @@ export const LogLevelList = [
value: 6,
label: 'Trace',
},
]
]
export const bytesToSize = (bytes) => {
if (bytes === 0) return '0 B';
var k = 1000, // or 1024
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}

View File

@ -70,7 +70,7 @@ func flushblacklist(c *gin.Context) {
newTime, err := config.BlackListAdd(ip, int32(life))
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "刷新IP有效期出错"})
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("刷新IP有效期出错:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime})

36
web/common.go Normal file
View File

@ -0,0 +1,36 @@
package web
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"github.com/gin-gonic/gin"
)
func getFileBase64(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("c.FormFile err:%s", err.Error())})
return
}
src, err := file.Open()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("file.Open err:%s", err.Error())})
return
}
defer src.Close()
fileBytes, err := io.ReadAll(src)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("ioutil.ReadAll err:%s", err.Error())})
return
}
fileBytesBase64Str := base64.StdEncoding.EncodeToString(fileBytes)
c.JSON(http.StatusOK, gin.H{"ret": 0, "file": file.Filename, "base64": fileBytesBase64Str})
}

View File

@ -91,9 +91,7 @@ func restoreConfigure(c *gin.Context) {
if conf.BaseConfigure.AdminAccount == "" ||
conf.BaseConfigure.AdminPassword == "" ||
conf.BaseConfigure.AdminWebListenPort <= 0 ||
conf.BaseConfigure.AdminWebListenPort >= 65536 ||
conf.BaseConfigure.GlobalMaxConnections <= 0 ||
conf.BaseConfigure.ProxyCountLimit <= 0 {
conf.BaseConfigure.AdminWebListenPort >= 65536 {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("配置文件[%s]参数有误", file.Filename)})
return
}

190
web/portforward.go Normal file
View File

@ -0,0 +1,190 @@
package web
import (
"fmt"
"log"
"net/http"
"strconv"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/socketproxy"
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
"github.com/gin-gonic/gin"
)
type ruleInfo struct {
config.PortForwardsRule
ProxyList []proxyInfo
LastLogs []any
}
type proxyInfo struct {
Proxy string
TrafficIn int64
TrafficOut int64
CurrentConnections int64
}
func PortForwardsRuleList(c *gin.Context) {
ruleRawList := config.GetPortForwardsRuleList()
var ruleList []ruleInfo
for i := range ruleRawList {
var proxyInfoList []proxyInfo
for j := range *ruleRawList[i].ReverseProxyList {
p := proxyInfo{
Proxy: (*ruleRawList[i].ReverseProxyList)[j].String(),
TrafficIn: (*ruleRawList[i].ReverseProxyList)[j].GetTrafficIn(),
TrafficOut: (*ruleRawList[i].ReverseProxyList)[j].GetTrafficOut(),
CurrentConnections: (*ruleRawList[i].ReverseProxyList)[j].GetCurrentConnections()}
proxyInfoList = append(proxyInfoList, p)
}
r := ruleInfo{
PortForwardsRule: ruleRawList[i],
ProxyList: proxyInfoList,
LastLogs: ruleRawList[i].GetLastLogs(ruleRawList[i].WebListShowLastLogMaxCount)}
ruleList = append(ruleList, r)
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "list": ruleList})
}
func PortForwardsRuleAdd(c *gin.Context) {
var newRule config.PortForwardsRule
err := c.Bind(&newRule)
if err != nil {
log.Printf("请求解析出错:%s", err.Error())
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
return
}
newRule.Key = stringsp.GetRandomString(16)
err = newRule.InitProxyList()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加转发规则过程初始化ProxyList出错:%s", err.Error())})
return
}
if int64(config.GetPortForwardsGlobalProxyCount()+newRule.ProxyCount()) > socketproxy.GetGlobalMaxPortForwardsCountLimit() {
c.JSON(http.StatusOK, gin.H{"ret": 3, "msg": "超出全局端口转发最大数量限制"})
return
}
err = config.PortForwardsRuleListAdd(&newRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 4, "msg": fmt.Sprintf("添加转发规则出错:%s", err.Error())})
return
}
config.StartAllSocketProxysByRuleKey(newRule.Key)
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func PortForwardsRuleAlter(c *gin.Context) {
var alterRule config.PortForwardsRule
err := c.Bind(&alterRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
return
}
err = alterRule.InitProxyList()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("修改转发规则时初始化代理列表出错:%s", err.Error())})
return
}
if int64(config.GetPortForwardsGlobalProxyCountExcept(alterRule.Key)+alterRule.ProxyCount()) > socketproxy.GetGlobalMaxPortForwardsCountLimit() {
c.JSON(http.StatusOK, gin.H{"ret": 3, "msg": "超出全局端口转发最大数量限制"})
return
}
config.StopAllSocketProxysByRuleKey(alterRule.Key)
err = config.UpdatePortForwardsRuleToPortForwardsRuleList(alterRule.Key, &alterRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 4, "msg": fmt.Sprintf("修改转发规则出错:%s", err.Error())})
return
}
if alterRule.Enable {
config.StartAllSocketProxysByRuleKey(alterRule.Key)
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func PortForwardsRuleEnable(c *gin.Context) {
enableStr := c.Query("enable")
key := c.Query("key")
var enable bool = false
if enableStr == "true" {
enable = true
}
err := config.EnablePortForwardsRuleByKey(key, enable)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("开关转发规则出错:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func PortForwardsRuleDelete(c *gin.Context) {
key := c.Query("key")
err := config.PortForwardsRuleListDelete(key)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除转发规则出错:%s", err.Error())})
return
}
config.TidyPortforwardLogsCache()
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func portforwardConfigure(c *gin.Context) {
conf := config.GetPortForwardsConfigure()
c.JSON(http.StatusOK, gin.H{"ret": 0, "configure": conf})
}
func alterPortForwardConfigure(c *gin.Context) {
var requestObj config.PortForwardsConfigure
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
return
}
err = config.SetPortForwardsConfigure(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "保存配置过程发生错误,请检测相关启动配置"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func getPortwardRuleLogs(c *gin.Context) {
key := c.Query("key")
pageSize, _ := strconv.Atoi(c.Query("pageSize"))
if pageSize <= 0 {
pageSize = 10
}
page, _ := strconv.Atoi(c.Query("page"))
if page <= 0 {
page = 1
}
rule := config.GetPortForwardsRuleByKey(key)
if rule == nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("找不到key:%s对应的规则", key)})
return
}
total, logList := rule.GetLogsBuffer().GetLogsByLimit(config.WebLogConvert, pageSize, page)
c.JSON(http.StatusOK, gin.H{"ret": 0, "total": total, "page": page, "pageSize": pageSize, "logs": logList})
}

View File

@ -69,19 +69,6 @@ func alterReverseProxyRule(c *gin.Context) {
return
}
// needStop := false
// if preRule != nil &&
// (preRule.Network != requestObj.Network ||
// preRule.ListenPort != requestObj.ListenPort ||
// preRule.Enable != requestObj.Enable ||
// !requestObj.Enable) {
// needStop = true
// }
// if needStop {
// }
reverseproxy.EnableRuleByKey(requestObj.RuleKey, false)
//reverseproxy.FlushCache(requestObj.RuleKey)
if requestObj.Enable {

View File

@ -1,200 +0,0 @@
package web
import (
"fmt"
"log"
"net/http"
"strings"
"github.com/gdy666/lucky/rule"
"github.com/gdy666/lucky/socketproxy"
"github.com/gin-gonic/gin"
)
func rulelist(c *gin.Context) {
ruleList, proxyListInfoMap := rule.GetRelayRuleList()
type ruleItem struct {
Name string `json:"Name"`
MainConfigure string `json:"Mainconfigure"`
RelayType string `json:"RelayType"`
ListenIP string `json:"ListenIP"`
ListenPorts string `json:"ListenPorts"`
TargetIP string `json:"TargetIP"`
TargetPorts string `json:"TargetPorts"`
BalanceTargetAddressList []string `json:"BalanceTargetAddressList"`
Options socketproxy.RelayRuleOptions `json:"Options"`
SubRuleList []rule.SubRelayRule `json:"SubRuleList"`
From string `json:"From"`
IsEnable bool `json:"Enable"`
ProxyList []rule.RelayRuleProxyInfo `json:"ProxyList"`
}
//proxyListInfoMap[(*ruleList)[i].MainConfigure]
var data []ruleItem
for i := range *ruleList {
item := ruleItem{
Name: (*ruleList)[i].Name,
MainConfigure: (*ruleList)[i].MainConfigure,
RelayType: (*ruleList)[i].RelayType,
ListenIP: (*ruleList)[i].ListenIP,
ListenPorts: (*ruleList)[i].ListenPorts,
TargetIP: (*ruleList)[i].TargetIP,
TargetPorts: (*ruleList)[i].TargetPorts,
Options: (*ruleList)[i].Options,
SubRuleList: (*ruleList)[i].SubRuleList,
From: (*ruleList)[i].From,
IsEnable: (*ruleList)[i].IsEnable,
ProxyList: proxyListInfoMap[(*ruleList)[i].MainConfigure],
BalanceTargetAddressList: (*ruleList)[i].BalanceTargetAddressList,
}
data = append(data, item)
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": data})
}
func addrule(c *gin.Context) {
var requestRule rule.RelayRule
err := c.BindJSON(&requestRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
return
}
dealRequestRule(&requestRule)
configureStr := requestRule.CreateMainConfigure()
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("创建转发规则出错:%s", err.Error())})
return
}
synsRes, err := rule.AddRuleToGlobalRuleList(true, *r)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("添加转发规则出错:%s", err.Error())})
return
}
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("启用规则出错:%s", err.Error())})
return
}
log.Printf("添加转发规则[%s][%s]成功", r.Name, r.MainConfigure)
if synsRes != "" {
synsRes = "保存配置文件出错,请检查配置文件设置"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "添加规则并启用成功", "syncres": synsRes})
}
func alterrule(c *gin.Context) {
var requestRule rule.RelayRule
err := c.BindJSON(&requestRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改请求解析出错:%s", err.Error())})
return
}
dealRequestRule(&requestRule)
//fmt.Printf("balance:%v\n", requestRule.BalanceTargetAddressList)
preConfigureStr := requestRule.MainConfigure
configureStr := requestRule.CreateMainConfigure()
// configureStr := fmt.Sprintf("%s@%s:%sto%s:%s",
// requestRule.RelayType,
// requestRule.ListenIP, requestRule.ListenPorts,
// requestRule.TargetIP, requestRule.TargetPorts)
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
return
}
syncSuccess, err := rule.AlterRuleInGlobalRuleListByKey(preConfigureStr, r)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
return
}
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("修改转发规则成功,但启用规则时出错:%s", err.Error())})
return
}
log.Printf("修改转发规则[%s][%s]成功", r.Name, r.MainConfigure)
synsRes := ""
if !syncSuccess {
synsRes = "同步修改规则数据到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "修改转发规则成功", "syncres": synsRes})
}
func deleterule(c *gin.Context) {
ruleKey := c.Query("rule")
rule.DisableRelayRuleByKey(ruleKey)
syncSuccess, err := rule.DeleteGlobalRuleByKey(ruleKey)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除转发规则出错:%s", err.Error())})
return
}
syncRes := ""
if !syncSuccess {
syncRes = "同步规则信息到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "删除成功", "syncres": syncRes})
}
func dealRequestRule(r *rule.RelayRule) {
r.ListenPorts = strings.TrimSpace(r.ListenPorts)
r.TargetPorts = strings.TrimSpace(r.TargetPorts)
r.ListenIP = strings.TrimSpace(r.ListenIP)
r.TargetIP = strings.TrimSpace(r.TargetIP)
r.RelayType = strings.TrimSpace(r.RelayType)
r.Name = strings.TrimSpace(r.Name)
}
func enablerule(c *gin.Context) {
enable := c.Query("enable")
key := c.Query("key")
var err error
var r *rule.RelayRule
var syncSuccess bool
if enable == "true" {
r, syncSuccess, err = rule.EnableRelayRuleByKey(key)
} else {
r, syncSuccess, err = rule.DisableRelayRuleByKey(key)
}
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("开关规则出错:%s", err.Error())})
return
}
log.Printf("[%s] relayRule[%s][%s]", enable, r.Name, r.MainConfigure)
syncRes := ""
if !syncSuccess {
syncRes = "同步规则状态到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "", "syncres": syncRes})
}

102
web/ssl.go Normal file
View File

@ -0,0 +1,102 @@
package web
import (
"fmt"
"log"
"net/http"
"github.com/gdy666/lucky/config"
"github.com/gin-gonic/gin"
)
func addSSL(c *gin.Context) {
var requestObj config.SSLCertficate
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
return
}
err = requestObj.Init()
if err != nil {
log.Printf("addSSL requestObj.Init() error:%s", err.Error())
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "证书和Key有误!"})
return
}
//fmt.Printf("CertsInfo:%v\n", *requestObj.CertsInfo)
err = config.SSLCertficateListAdd(&requestObj)
if err != nil {
log.Printf("config.SSLCertficateListAdd error:%s", err.Error())
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加SSL证书出错!:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
type sslResInfo struct {
Key string `json:"Key"`
Remark string `json:"Remark"`
Enable bool `json:"Enable"`
AddTime string `json:"AddTime"`
CertsInfo *[]config.CertInfo `json:"CertsInfo"`
}
func getSSLCertficateList(c *gin.Context) {
rawList := config.GetSSLCertficateList()
var res []sslResInfo
for i := range rawList {
info := sslResInfo{
Key: rawList[i].Key,
Remark: rawList[i].Remark,
Enable: rawList[i].Enable,
AddTime: rawList[i].AddTime,
CertsInfo: rawList[i].CertsInfo,
}
res = append(res, info)
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "list": res})
}
func alterSSLCertficate(c *gin.Context) {
key := c.Query("key")
field := c.Query("field")
value := c.Query("value")
var err error
switch field {
case "enable":
{
enable := false
if value == "true" {
enable = true
}
err = config.SSLCertficateEnable(key, enable)
}
case "remark":
{
err = config.SSLCertficateAlterRemark(key, value)
}
default:
err = fmt.Errorf("不支持修改的字段:%s", field)
}
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func deleteSSLCertficate(c *gin.Context) {
key := c.Query("key")
err := config.SSLCertficateListDelete(key)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}

View File

@ -1,6 +1,7 @@
package web
import (
"crypto/tls"
"embed"
"fmt"
"io"
@ -57,10 +58,10 @@ func init() {
}
func RunAdminWeb(listen string, logMaxSize int) {
func RunAdminWeb(conf *config.BaseConfigure) {
//gin.Default()
logBuffer.SetBufferSize(logMaxSize)
logBuffer.SetBufferSize(conf.LogMaxSize)
gin.SetMode(gin.ReleaseMode)
r := gin.New()
@ -87,11 +88,14 @@ func RunAdminWeb(listen string, logMaxSize int) {
authorized.GET("/api/status", status)
authorized.GET("/api/test", test)
authorized.GET("/api/rulelist", rulelist)
authorized.POST("/api/rule", addrule)
authorized.DELETE("/api/rule", deleterule)
authorized.PUT("/api/rule", alterrule)
authorized.GET("/api/rule/enable", enablerule)
authorized.GET("/api/portforwards", PortForwardsRuleList)
authorized.POST("/api/portforward", PortForwardsRuleAdd)
authorized.DELETE("/api/portforward", PortForwardsRuleDelete)
authorized.PUT("/api/portforward", PortForwardsRuleAlter)
authorized.GET("/api/portforward/enable", PortForwardsRuleEnable)
authorized.GET("/api/portforward/configure", portforwardConfigure)
authorized.PUT("/api/portforward/configure", alterPortForwardConfigure)
authorized.GET("/api/portforward/logs", getPortwardRuleLogs)
authorized.GET("/api/baseconfigure", baseconfigure)
authorized.PUT("/api/baseconfigure", alterBaseConfigure)
@ -125,9 +129,22 @@ func RunAdminWeb(listen string, logMaxSize int) {
authorized.GET("/api/reverseproxyrule/enable", enableReverseProxyRule)
authorized.GET("/api/reverseproxyrule/logs", getReverseProxyLog)
authorized.POST("/api/ssl", addSSL)
authorized.GET("/api/ssl", getSSLCertficateList)
authorized.PUT("/api/ssl", alterSSLCertficate)
authorized.DELETE("/api/ssl", deleteSSLCertficate)
authorized.POST("/api/wol/device", addWOLDevice)
authorized.GET("/api/wol/device/wakeup", WOLDeviceWakeUp)
authorized.GET("/api/wol/devices", getWOLDeviceList)
authorized.PUT("/api/wol/device", alterWOLDevice)
authorized.DELETE("/api/wol/device", deleteWOLDevice)
authorized.GET("/api/info", info)
authorized.GET("/api/configure", configure)
authorized.POST("/api/configure", restoreConfigure)
authorized.POST("/api/getfilebase64", getFileBase64)
authorized.GET("/api/restoreconfigureconfirm", restoreConfigureConfirm)
r.PUT("/api/logout", logout)
}
@ -140,11 +157,39 @@ func RunAdminWeb(listen string, logMaxSize int) {
//r.Use(func() *gin.Context {})
err := r.Run(listen)
go func() {
httpListen := fmt.Sprintf(":%d", conf.AdminWebListenPort)
log.Printf("AdminWeb(Http) listen on %s", httpListen)
err := r.Run(httpListen)
if err != nil {
log.Printf("Admin Http Listen error:%s", err.Error())
os.Exit(1)
}
}()
if err != nil {
log.Printf("http.ListenAndServe error:%s", err.Error())
os.Exit(1)
if conf.AdminWebListenTLS {
certlist := config.GetValidSSLCertficateList()
if len(certlist) <= 0 {
log.Printf("可用SSL证书列表为空,AdminWeb(Https) 监听服务中止运行")
return
}
httpsListen := fmt.Sprintf(":%d", conf.AdminWebListenHttpsPort)
server := &http.Server{
Addr: httpsListen,
Handler: r,
}
server.TLSConfig = &tls.Config{}
server.TLSConfig.Certificates = certlist
ln, err := net.Listen("tcp", httpsListen)
if err != nil {
log.Fatalf("Admin Https Listen error:%s", err.Error())
}
log.Printf("AdminWeb(Https) listen on %s", httpsListen)
err = server.ServeTLS(ln, "", "")
if err != nil {
log.Printf("AdminWeb(Https) Server error:%s", err.Error())
}
}
}
@ -382,8 +427,9 @@ func status(c *gin.Context) {
respMap["currentProcessUsedCPU"] = fmt.Sprintf("%.2f%%", GetCurrentProcessCPUPrecent())
respMap["goroutine"] = fmt.Sprintf("%d", runtime.NumGoroutine())
respMap["processUsedMem"] = stringsp.BinaryUnitToStr(currentProcessMem)
respMap["currentConnections"] = fmt.Sprintf("%d", socketproxy.GetGlobalConnections())
respMap["maxConnections"] = fmt.Sprintf("%d", socketproxy.GetGlobalMaxConnections())
respMap["currentTCPConnections"] = fmt.Sprintf("%d", socketproxy.GetGlobalTCPPortForwardConnections())
respMap["currentUDPConnections"] = fmt.Sprintf("%d", socketproxy.GetGlobalUDPPortForwardGroutineCount())
respMap["maxTCPConnections"] = fmt.Sprintf("%d", socketproxy.GetGlobalTCPPortforwardMaxConnections())
respMap["usedCPU"] = fmt.Sprintf("%.2f%%", GetCpuPercent())
respMap["runTime"] = appInfo.RunTime
//respMap["proxysStatus"] = proxyStatusList

103
web/wol.go Normal file
View File

@ -0,0 +1,103 @@
package web
import (
"fmt"
"log"
"net/http"
"github.com/gdy666/lucky/config"
"github.com/gin-gonic/gin"
)
func addWOLDevice(c *gin.Context) {
var requestObj config.WOLDevice
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
return
}
err = checkWolDevice(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加网络唤醒设备出错:%s", err.Error())})
return
}
err = config.WOLDeviceListAdd(&requestObj)
if err != nil {
log.Printf("config.WOLDeviceListAdd error:%s", err.Error())
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加网络唤醒设备出错!:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func getWOLDeviceList(c *gin.Context) {
list := config.GetWOLDeviceList()
c.JSON(http.StatusOK, gin.H{"ret": 0, "list": list})
}
func alterWOLDevice(c *gin.Context) {
var requestObj config.WOLDevice
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
return
}
err = checkWolDevice(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("修改网络唤醒设备出错:%s", err.Error())})
return
}
err = config.WOLDeviceListAlter(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改网络唤醒设备配置失败:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func deleteWOLDevice(c *gin.Context) {
key := c.Query("key")
err := config.WOLDeviceListDelete(key)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除网络唤醒设备失败:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func WOLDeviceWakeUp(c *gin.Context) {
key := c.Query("key")
device := config.GetWOLDeviceByKey(key)
if device == nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("找不到Key对应的设备,唤醒失败")})
return
}
err := device.WakeUp()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "唤醒失败"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func checkWolDevice(d *config.WOLDevice) error {
// if strings.TrimSpace(d.DeviceName) == "" {
// return fmt.Errorf("设备名称不能为空")
// }
if d.Port <= 0 || d.Port > 065535 {
d.Port = 9
}
if d.Repeat <= 0 || d.Repeat > 10 {
d.Repeat = 5
}
return nil
}

View File

@ -1,7 +0,0 @@
//go:build !adminweb
// +build !adminweb
package main
func RunAdminWeb(listenPort int, logMaxSize int) {
}