nps/lib/config/config.go

330 lines
7.7 KiB
Go

package config
import (
"errors"
"fmt"
"regexp"
"strings"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
)
type CommonConfig struct {
Server string
VKey string
Tp string //bridgeType kcp or tcp
AutoReconnection bool
ProxyUrl string
Client *file.Client
DisconnectTime int
}
type LocalServer struct {
Type string
Port int
Ip string
Password string
Target string
}
type Config struct {
content string
title []string
CommonConfig *CommonConfig
Hosts []*file.Host
Tasks []*file.Tunnel
Healths []*file.Health
LocalServer []*LocalServer
}
func NewConfig(path string) (c *Config, err error) {
c = new(Config)
var b []byte
if b, err = common.ReadAllFromFile(path); err != nil {
return
} else {
if c.content, err = common.ParseStr(string(b)); err != nil {
return nil, err
}
if c.title, err = getAllTitle(c.content); err != nil {
return
}
var nowIndex int
var nextIndex int
var nowContent string
for i := 0; i < len(c.title); i++ {
nowIndex = strings.Index(c.content, c.title[i]) + len(c.title[i])
if i < len(c.title)-1 {
nextIndex = strings.Index(c.content, c.title[i+1])
} else {
nextIndex = len(c.content)
}
nowContent = c.content[nowIndex:nextIndex]
if strings.Index(getTitleContent(c.title[i]), "secret") == 0 && !strings.Contains(nowContent, "mode") {
local := delLocalService(nowContent)
local.Type = "secret"
c.LocalServer = append(c.LocalServer, local)
continue
}
//except mode
if strings.Index(getTitleContent(c.title[i]), "p2p") == 0 && !strings.Contains(nowContent, "mode") {
local := delLocalService(nowContent)
local.Type = "p2p"
c.LocalServer = append(c.LocalServer, local)
continue
}
//health set
if strings.Index(getTitleContent(c.title[i]), "health") == 0 {
c.Healths = append(c.Healths, dealHealth(nowContent))
continue
}
switch c.title[i] {
case "[common]":
c.CommonConfig = dealCommon(nowContent)
default:
if strings.Index(nowContent, "host") > -1 {
h := dealHost(nowContent)
h.Remark = getTitleContent(c.title[i])
c.Hosts = append(c.Hosts, h)
} else {
t := dealTunnel(nowContent)
t.Remark = getTitleContent(c.title[i])
c.Tasks = append(c.Tasks, t)
}
}
}
}
return
}
func getTitleContent(s string) string {
re, _ := regexp.Compile(`[\[\]]`)
return re.ReplaceAllString(s, "")
}
func dealCommon(s string) *CommonConfig {
c := &CommonConfig{}
c.Client = file.NewClient("", true, true)
c.Client.Cnf = new(file.Config)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch item[0] {
case "server_addr":
c.Server = item[1]
case "vkey":
c.VKey = item[1]
case "conn_type":
c.Tp = item[1]
case "auto_reconnection":
c.AutoReconnection = common.GetBoolByStr(item[1])
case "basic_username":
c.Client.Cnf.U = item[1]
case "basic_password":
c.Client.Cnf.P = item[1]
case "web_password":
c.Client.WebPassword = item[1]
case "web_username":
c.Client.WebUserName = item[1]
case "compress":
c.Client.Cnf.Compress = common.GetBoolByStr(item[1])
case "crypt":
c.Client.Cnf.Crypt = common.GetBoolByStr(item[1])
case "proxy_url":
c.ProxyUrl = item[1]
case "rate_limit":
c.Client.RateLimit = common.GetIntNoErrByStr(item[1])
case "flow_limit":
c.Client.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[1]))
case "max_conn":
c.Client.MaxConn = common.GetIntNoErrByStr(item[1])
case "remark":
c.Client.Remark = item[1]
case "pprof_addr":
common.InitPProfFromArg(item[1])
case "disconnect_timeout":
c.DisconnectTime = common.GetIntNoErrByStr(item[1])
}
}
return c
}
func dealHost(s string) *file.Host {
h := &file.Host{}
h.Target = new(file.Target)
h.Scheme = "all"
var headerChange string
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "host":
h.Host = item[1]
case "target_addr":
h.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
case "host_change":
h.HostChange = item[1]
case "scheme":
h.Scheme = item[1]
case "location":
h.Location = item[1]
default:
if strings.Contains(item[0], "header") {
headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n"
}
h.HeaderChange = headerChange
}
}
return h
}
func dealHealth(s string) *file.Health {
h := &file.Health{}
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "health_check_timeout":
h.HealthCheckTimeout = common.GetIntNoErrByStr(item[1])
case "health_check_max_failed":
h.HealthMaxFail = common.GetIntNoErrByStr(item[1])
case "health_check_interval":
h.HealthCheckInterval = common.GetIntNoErrByStr(item[1])
case "health_http_url":
h.HttpHealthUrl = item[1]
case "health_check_type":
h.HealthCheckType = item[1]
case "health_check_target":
h.HealthCheckTarget = item[1]
}
}
return h
}
func dealTunnel(s string) *file.Tunnel {
t := &file.Tunnel{}
t.Target = new(file.Target)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "server_port":
t.Ports = item[1]
case "server_ip":
t.ServerIp = item[1]
case "mode":
t.Mode = item[1]
case "target_addr":
t.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
case "target_port":
t.Target.TargetStr = item[1]
case "target_ip":
t.TargetAddr = item[1]
case "password":
t.Password = item[1]
case "local_path":
t.LocalPath = item[1]
case "strip_pre":
t.StripPre = item[1]
case "multi_account":
t.MultiAccount = &file.MultiAccount{}
if common.FileExists(item[1]) {
if b, err := common.ReadAllFromFile(item[1]); err != nil {
panic(err)
} else {
if content, err := common.ParseStr(string(b)); err != nil {
panic(err)
} else {
t.MultiAccount.AccountMap = dealMultiUser(content)
}
}
}
}
}
return t
}
func dealMultiUser(s string) map[string]string {
multiUserMap := make(map[string]string)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
multiUserMap[strings.TrimSpace(item[0])] = item[1]
}
return multiUserMap
}
func delLocalService(s string) *LocalServer {
l := new(LocalServer)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch item[0] {
case "local_port":
l.Port = common.GetIntNoErrByStr(item[1])
case "local_ip":
l.Ip = item[1]
case "password":
l.Password = item[1]
case "target_addr":
l.Target = item[1]
}
}
return l
}
func getAllTitle(content string) (arr []string, err error) {
var re *regexp.Regexp
re, err = regexp.Compile(`\[.+?\]`)
if err != nil {
return
}
arr = re.FindAllString(content, -1)
m := make(map[string]bool)
for _, v := range arr {
if _, ok := m[v]; ok {
err = errors.New(fmt.Sprintf("Item names %s are not allowed to be duplicated", v))
return
}
m[v] = true
}
return
}
func splitStr(s string) (configDataArr []string) {
if common.IsWindows() {
configDataArr = strings.Split(s, "\r\n")
}
if len(configDataArr) < 3 {
configDataArr = strings.Split(s, "\n")
}
return
}