From ce8fde793cdc794c7e3bcd3f7a20427acd288f4a Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 25 Jan 2018 23:05:07 +0800 Subject: [PATCH] new feature: range section for mapping range ports --- models/config/proxy.go | 74 +++++++++++++++++++++++++++++++--- models/config/server_common.go | 47 +++++---------------- utils/util/util.go | 47 +++++++++++++++++++++ utils/util/util_test.go | 26 ++++++++++++ 4 files changed, 151 insertions(+), 43 deletions(-) diff --git a/models/config/proxy.go b/models/config/proxy.go index 022e64f4..b506c435 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -22,6 +22,7 @@ import ( "github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/models/msg" + "github.com/fatedier/frp/utils/util" ini "github.com/vaughan0/go-ini" ) @@ -770,6 +771,38 @@ func (cfg *XtcpProxyConf) Check() (err error) { return } +func ParseRangeSection(name string, section ini.Section) (sections map[string]ini.Section, err error) { + localPorts, errRet := util.ParseRangeNumbers(section["local_port"]) + if errRet != nil { + err = fmt.Errorf("Parse conf error: range section [%s] local_port invalid, %v", name, errRet) + return + } + + remotePorts, errRet := util.ParseRangeNumbers(section["remote_port"]) + if errRet != nil { + err = fmt.Errorf("Parse conf error: range section [%s] remote_port invalid, %v", name, errRet) + return + } + if len(localPorts) != len(remotePorts) { + err = fmt.Errorf("Parse conf error: range section [%s] local ports number should be same with remote ports number", name) + return + } + if len(localPorts) == 0 { + err = fmt.Errorf("Parse conf error: range section [%s] local_port and remote_port is necessary") + return + } + + sections = make(map[string]ini.Section) + for i, port := range localPorts { + subName := fmt.Sprintf("%s_%d", name, i) + subSection := copySection(section) + subSection["local_port"] = fmt.Sprintf("%d", port) + subSection["remote_port"] = fmt.Sprintf("%d", remotePorts[i]) + sections[subName] = subSection + } + return +} + // if len(startProxy) is 0, start all // otherwise just start proxies in startProxy map func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) ( @@ -786,22 +819,51 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s proxyConfs = make(map[string]ProxyConf) visitorConfs = make(map[string]ProxyConf) for name, section := range conf { + if name == "common" { + continue + } + _, shouldStart := startProxy[name] - if name != "common" && (startAll || shouldStart) { + if !startAll && !shouldStart { + continue + } + + subSections := make(map[string]ini.Section) + + if strings.HasPrefix(name, "range:") { + // range section + rangePrefix := strings.TrimSpace(strings.TrimPrefix(name, "range:")) + subSections, err = ParseRangeSection(rangePrefix, section) + if err != nil { + return + } + } else { + subSections[name] = section + } + + for subName, subSection := range subSections { // some proxy or visotr configure may be used this prefix - section["prefix"] = prefix - cfg, err := NewProxyConfFromFile(name, section) + subSection["prefix"] = prefix + cfg, err := NewProxyConfFromFile(subName, subSection) if err != nil { return proxyConfs, visitorConfs, err } - role := section["role"] + role := subSection["role"] if role == "visitor" { - visitorConfs[prefix+name] = cfg + visitorConfs[prefix+subName] = cfg } else { - proxyConfs[prefix+name] = cfg + proxyConfs[prefix+subName] = cfg } } } return } + +func copySection(section ini.Section) (out ini.Section) { + out = make(ini.Section) + for k, v := range section { + out[k] = v + } + return +} diff --git a/models/config/server_common.go b/models/config/server_common.go index 37892b4e..614b95cb 100644 --- a/models/config/server_common.go +++ b/models/config/server_common.go @@ -20,6 +20,8 @@ import ( "strings" ini "github.com/vaughan0/go-ini" + + "github.com/fatedier/frp/utils/util" ) var ServerCommonCfg *ServerCommonConf @@ -238,43 +240,14 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) { allowPortsStr, ok := conf.Get("common", "privilege_allow_ports") if ok { // e.g. 1000-2000,2001,2002,3000-4000 - portRanges := strings.Split(allowPortsStr, ",") - for _, portRangeStr := range portRanges { - // 1000-2000 or 2001 - portArray := strings.Split(portRangeStr, "-") - // length: only 1 or 2 is correct - rangeType := len(portArray) - if rangeType == 1 { - // single port - singlePort, errRet := strconv.ParseInt(portArray[0], 10, 64) - if errRet != nil { - err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet) - return - } - cfg.PrivilegeAllowPorts[int(singlePort)] = struct{}{} - } else if rangeType == 2 { - // range ports - min, errRet := strconv.ParseInt(portArray[0], 10, 64) - if errRet != nil { - err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet) - return - } - max, errRet := strconv.ParseInt(portArray[1], 10, 64) - if errRet != nil { - err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet) - return - } - if max < min { - err = fmt.Errorf("Parse conf error: privilege_allow_ports range incorrect") - return - } - for i := min; i <= max; i++ { - cfg.PrivilegeAllowPorts[int(i)] = struct{}{} - } - } else { - err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect") - return - } + ports, errRet := util.ParseRangeNumbers(allowPortsStr) + if errRet != nil { + err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet) + return + } + + for _, port := range ports { + cfg.PrivilegeAllowPorts[int(port)] = struct{}{} } } } diff --git a/utils/util/util.go b/utils/util/util.go index 4439f1aa..7ea4e83c 100644 --- a/utils/util/util.go +++ b/utils/util/util.go @@ -19,6 +19,8 @@ import ( "crypto/rand" "encoding/hex" "fmt" + "strconv" + "strings" ) // RandId return a rand string used in frp. @@ -54,3 +56,48 @@ func CanonicalAddr(host string, port int) (addr string) { } return } + +func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) { + rangeStr = strings.TrimSpace(rangeStr) + numbers = make([]int64, 0) + // e.g. 1000-2000,2001,2002,3000-4000 + numRanges := strings.Split(rangeStr, ",") + for _, numRangeStr := range numRanges { + // 1000-2000 or 2001 + numArray := strings.Split(numRangeStr, "-") + // length: only 1 or 2 is correct + rangeType := len(numArray) + if rangeType == 1 { + // single number + singleNum, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64) + if errRet != nil { + err = fmt.Errorf("range number is invalid, %v", errRet) + return + } + numbers = append(numbers, singleNum) + } else if rangeType == 2 { + // range numbers + min, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64) + if errRet != nil { + err = fmt.Errorf("range number is invalid, %v", errRet) + return + } + max, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64) + if errRet != nil { + err = fmt.Errorf("range number is invalid, %v", errRet) + return + } + if max < min { + err = fmt.Errorf("range number is invalid") + return + } + for i := min; i <= max; i++ { + numbers = append(numbers, i) + } + } else { + err = fmt.Errorf("range number is invalid") + return + } + } + return +} diff --git a/utils/util/util_test.go b/utils/util/util_test.go index 8210c613..a7518f6f 100644 --- a/utils/util/util_test.go +++ b/utils/util/util_test.go @@ -20,3 +20,29 @@ func TestGetAuthKey(t *testing.T) { t.Log(key) assert.Equal("6df41a43725f0c770fd56379e12acf8c", key) } + +func TestParseRangeNumbers(t *testing.T) { + assert := assert.New(t) + numbers, err := ParseRangeNumbers("2-5") + if assert.NoError(err) { + assert.Equal([]int64{2, 3, 4, 5}, numbers) + } + + numbers, err = ParseRangeNumbers("1") + if assert.NoError(err) { + assert.Equal([]int64{1}, numbers) + } + + numbers, err = ParseRangeNumbers("3-5,8") + if assert.NoError(err) { + assert.Equal([]int64{3, 4, 5, 8}, numbers) + } + + numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ") + if assert.NoError(err) { + assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers) + } + + _, err = ParseRangeNumbers("3-a") + assert.Error(err) +}