diff --git a/config.go b/config.go index b66e1e95..af6f7789 100644 --- a/config.go +++ b/config.go @@ -39,13 +39,19 @@ var defaultTunnelAllowedPort = []string{ "2401", "3690", "9418", // cvspserver, svn, git } +type portSegment struct { + minPort int + maxPort int +} + type Config struct { RcFile string // config file LogFile string // path for log file AlwaysProxy bool // whether we should alwyas use parent proxy LoadBalance LoadBalanceMode // select load balance mode - TunnelAllowedPort map[string]bool // allowed ports to create tunnel + TunnelAllowedPortSegment []*portSegment // allowed port segments to create tunnel + TunnelAllowedPort map[string]bool // allowed ports to create tunnel SshServer []string @@ -98,6 +104,7 @@ func initConfig(rcFile string) { config.DialTimeout = defaultDialTimeout config.ReadTimeout = defaultReadTimeout + config.TunnelAllowedPortSegment = []*portSegment{} config.TunnelAllowedPort = make(map[string]bool) for _, port := range defaultTunnelAllowedPort { config.TunnelAllowedPort[port] = true @@ -379,14 +386,55 @@ func (p configParser) ParseAddrInPAC(val string) { } } +func (c Config) IsTunnelAllowedPort(s string) bool { + if config.TunnelAllowedPort[s] { + return true + } + if port, err := strconv.Atoi(s); err == nil { + for _, pSeg := range config.TunnelAllowedPortSegment { + if port >= pSeg.minPort && port <= pSeg.maxPort { + return true + } + } + } + return false +} + func (p configParser) ParseTunnelAllowedPort(val string) { arr := strings.Split(val, ",") + for i, s := range arr { + arr[i] = strings.TrimSpace(s) + } + for i, s := range arr { + var min, max int + if strings.Contains(s, "-") { + if _, err := fmt.Sscanf(s, "%d-%d", &min, &max); err != nil || min > max { + Fatal("tunnel allowed port segments", err) + } + if min == max { + arr[i] = strconv.Itoa(min) + } else { + config.TunnelAllowedPortSegment = append(config.TunnelAllowedPortSegment, &portSegment{min, max}) + arr[i] = "" + } + } + } for _, s := range arr { - s = strings.TrimSpace(s) - if _, err := strconv.Atoi(s); err != nil { - Fatal("tunnel allowed ports", err) + if s != "" { + if _, err := strconv.Atoi(s); err != nil { + Fatal("tunnel allowed ports", err) + } + config.TunnelAllowedPort[s] = true + } + } + for s := range config.TunnelAllowedPort { + if p1, err := strconv.Atoi(s); err == nil { + for _, p2 := range config.TunnelAllowedPortSegment { + if p1 >= p2.minPort && p1 <= p2.maxPort { + delete(config.TunnelAllowedPort, s) + } + } } - config.TunnelAllowedPort[s] = true } } diff --git a/config_test.go b/config_test.go index 4bb4f084..affc7bf7 100644 --- a/config_test.go +++ b/config_test.go @@ -26,10 +26,11 @@ func TestParseListen(t *testing.T) { func TestTunnelAllowedPort(t *testing.T) { initConfig("") parser := configParser{} - parser.ParseTunnelAllowedPort("1, 2, 3, 4, 5") + parser.ParseTunnelAllowedPort("1, 2, 3, 4, 5, 100-500, 600-888") parser.ParseTunnelAllowedPort("6") parser.ParseTunnelAllowedPort("7") parser.ParseTunnelAllowedPort("8") + parser.ParseTunnelAllowedPort("1000-3333") testData := []struct { port string @@ -41,12 +42,17 @@ func TestTunnelAllowedPort(t *testing.T) { {"3", true}, {"5", true}, {"7", true}, + {"342", true}, + {"777", true}, + {"2332", true}, + + {"523", false}, {"8080", false}, {"8388", false}, } for _, td := range testData { - allowed := config.TunnelAllowedPort[td.port] + allowed := config.IsTunnelAllowedPort(td.port) if allowed != td.allowed { t.Errorf("port %s allowed %v, got %v\n", td.port, td.allowed, allowed) } diff --git a/doc/sample-config/rc b/doc/sample-config/rc index 484dd019..c2c3099f 100644 --- a/doc/sample-config/rc +++ b/doc/sample-config/rc @@ -130,11 +130,11 @@ listen = http://127.0.0.1:7777 # 检测超时时间使用的网站,最好使用能快速访问的站点 #estimateTarget = example.com -# 允许建立隧道连接的端口,多个端口用逗号分隔,可重复多次 +# 允许建立隧道连接的端口,多个端口用逗号分隔,可重复多次,支持端口段 # 默认总是允许下列服务的端口: ssh, http, https, rsync, imap, pop, jabber, cvs, git, svn # 如需允许其他端口,请用该选项添加 # 限制隧道连接的端口可以防止将运行 COW 的服务器上只监听本机 ip 的服务暴露给外部 -#tunnelAllowedPort = 80, 443 +#tunnelAllowedPort = 80, 443, 100-500, 2333-6666 # GFW 会使 DNS 解析超时,也可能返回错误的地址,能连接但是读不到任何内容 # 下面两个值改小一点可以加速检测网站是否被墙,但网络情况差时可能误判 diff --git a/doc/sample-config/rc-en b/doc/sample-config/rc-en index ee2857d9..a259e54c 100644 --- a/doc/sample-config/rc-en +++ b/doc/sample-config/rc-en @@ -156,7 +156,7 @@ listen = http://127.0.0.1:7777 # ssh, http, https, rsync, imap, pop, jabber, cvs, git, svn # # Limiting ports for tunneling prevents exposing internal services to outside. -#tunnelAllowedPort = 80, 443 +#tunnelAllowedPort = 80, 443, 100-500, 2333-6666 # GFW may timeout DNS query, or return wrong server address which can connect # but blocks on read forever. diff --git a/proxy.go b/proxy.go index 601ac65e..378cf27b 100644 --- a/proxy.go +++ b/proxy.go @@ -495,7 +495,7 @@ func (c *clientConn) serve() { authed = true } - if r.isConnect && !config.TunnelAllowedPort[r.URL.Port] { + if r.isConnect && !config.IsTunnelAllowedPort(r.URL.Port) { sendErrorPage(c, statusForbidden, "Forbidden tunnel port", genErrMsg(&r, nil, "Please contact proxy admin.")) return