Browse Source

new feature: range section for mapping range ports

fatedier 7 years ago
parent
commit
ce8fde793c
4 changed files with 151 additions and 43 deletions
  1. 68 6
      models/config/proxy.go
  2. 10 37
      models/config/server_common.go
  3. 47 0
      utils/util/util.go
  4. 26 0
      utils/util/util_test.go

+ 68 - 6
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
+}

+ 10 - 37
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{}{}
 			}
 		}
 	}

+ 47 - 0
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
+}

+ 26 - 0
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)
+}