From 396e148f805fd83626868303f041f82c6d6f83db Mon Sep 17 00:00:00 2001
From: Maodanping <673698750@qq.com>
Date: Sat, 5 Nov 2016 14:15:16 +0800
Subject: [PATCH 1/4] add subdomain configuration; add conn auth timeout

---
 conf/frpc.ini               |  3 +++
 conf/frps.ini               |  4 ++++
 src/cmd/frpc/control.go     |  1 +
 src/cmd/frps/control.go     | 13 +++++++++----
 src/models/client/config.go |  8 ++++++++
 src/models/config/config.go |  1 +
 src/models/msg/msg.go       |  1 +
 src/models/server/config.go | 12 ++++++++++++
 src/models/server/server.go |  6 ++++++
 9 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/conf/frpc.ini b/conf/frpc.ini
index 083f84f0..e9aa2ee5 100644
--- a/conf/frpc.ini
+++ b/conf/frpc.ini
@@ -40,6 +40,8 @@ pool_count = 20
 # if not set, you can access this custom_domains without certification
 http_username = admin
 http_password = admin
+# if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
+subdomain = test
 
 [web02]
 type = http
@@ -64,3 +66,4 @@ local_port = 80
 use_gzip = true
 custom_domains = web03.yourdomain.com
 host_header_rewrite = example.com
+subdomain = dev
diff --git a/conf/frps.ini b/conf/frps.ini
index 6163bc98..8835f634 100644
--- a/conf/frps.ini
+++ b/conf/frps.ini
@@ -26,6 +26,10 @@ privilege_token = 12345678
 privilege_allow_ports = 2000-3000,3001,3003,4000-50000
 # pool_count in each proxy will change to max_pool_count if they exceed the maximum value
 max_pool_count = 100
+# conn_timeout set the timeout interval (seconds) when the frpc connects frps
+conn_timeout = 10
+# domain for frps
+domain = codermao.com
 
 # ssh is the proxy name, client will use this name and auth_token to connect to server
 [ssh]
diff --git a/src/cmd/frpc/control.go b/src/cmd/frpc/control.go
index dcb8b0d7..db9929e4 100644
--- a/src/cmd/frpc/control.go
+++ b/src/cmd/frpc/control.go
@@ -152,6 +152,7 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) {
 		HostHeaderRewrite: cli.HostHeaderRewrite,
 		HttpUserName:      cli.HttpUserName,
 		HttpPassWord:      cli.HttpPassWord,
+		SubDomain:         cli.SubDomain,
 		Timestamp:         nowTime,
 	}
 	if cli.PrivilegeMode {
diff --git a/src/cmd/frps/control.go b/src/cmd/frps/control.go
index f2b25eab..851ceee1 100644
--- a/src/cmd/frps/control.go
+++ b/src/cmd/frps/control.go
@@ -221,8 +221,8 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 	nowTime := time.Now().Unix()
 	if req.PrivilegeMode {
 		privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeToken + fmt.Sprintf("%d", req.Timestamp))
-		// privilegeKey avaiable in 15 minutes
-		if nowTime-req.Timestamp > 15*60 {
+		// privilegeKey unavaiable after server.CtrlConnTimeout seconds
+		if server.CtrlConnTimeout != 0 && nowTime-req.Timestamp > server.CtrlConnTimeout {
 			info = fmt.Sprintf("ProxyName [%s], privilege mode authorization timeout", req.ProxyName)
 			log.Warn(info)
 			return
@@ -234,8 +234,8 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 		}
 	} else {
 		authKey := pcrypto.GetAuthKey(req.ProxyName + s.AuthToken + fmt.Sprintf("%d", req.Timestamp))
-		// authKey avaiable in 15 minutes
-		if nowTime-req.Timestamp > 15*60 {
+		// privilegeKey unavaiable after server.CtrlConnTimeout seconds
+		if server.CtrlConnTimeout != 0 && nowTime-req.Timestamp > server.CtrlConnTimeout {
 			info = fmt.Sprintf("ProxyName [%s], authorization timeout", req.ProxyName)
 			log.Warn(info)
 			return
@@ -289,6 +289,10 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 		s.HostHeaderRewrite = req.HostHeaderRewrite
 		s.HttpUserName = req.HttpUserName
 		s.HttpPassWord = req.HttpPassWord
+		// package URL
+		if req.SubDomain != "" {
+			s.SubDomain = req.SubDomain + "." + server.Domain
+		}
 		if req.PoolCount > server.MaxPoolCount {
 			s.PoolCount = server.MaxPoolCount
 		} else if req.PoolCount < 0 {
@@ -302,6 +306,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 			log.Warn(info)
 			return
 		}
+		log.Info("serverProxy: %+v", s)
 
 		// update metric's proxy status
 		metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip, s.PrivilegeMode, s.CustomDomains, s.ListenPort)
diff --git a/src/models/client/config.go b/src/models/client/config.go
index 417ea3b2..e26ca782 100644
--- a/src/models/client/config.go
+++ b/src/models/client/config.go
@@ -166,6 +166,11 @@ func LoadConf(confFile string) (err error) {
 				if ok {
 					proxyClient.HttpPassWord = tmpStr
 				}
+				// subdomain
+				tmpStr, ok = section["subdomain"]
+				if ok {
+					proxyClient.SubDomain = tmpStr
+				}
 			}
 
 			// privilege_mode
@@ -219,6 +224,9 @@ func LoadConf(confFile string) (err error) {
 					} else {
 						return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name)
 					}
+
+					// subdomain
+					proxyClient.SubDomain, ok = section["subdomain"]
 				} else if proxyClient.Type == "https" {
 					// custom_domains
 					domainStr, ok := section["custom_domains"]
diff --git a/src/models/config/config.go b/src/models/config/config.go
index 9cf0071e..f71d24af 100644
--- a/src/models/config/config.go
+++ b/src/models/config/config.go
@@ -26,4 +26,5 @@ type BaseConf struct {
 	HostHeaderRewrite string
 	HttpUserName      string
 	HttpPassWord      string
+	SubDomain         string
 }
diff --git a/src/models/msg/msg.go b/src/models/msg/msg.go
index f065df13..64119ec5 100644
--- a/src/models/msg/msg.go
+++ b/src/models/msg/msg.go
@@ -37,6 +37,7 @@ type ControlReq struct {
 	HostHeaderRewrite string   `json:"host_header_rewrite"`
 	HttpUserName      string   `json:"http_username"`
 	HttpPassWord      string   `json:"http_password"`
+	SubDomain         string   `json:"subdomain"`
 	Timestamp         int64    `json:"timestamp"`
 }
 
diff --git a/src/models/server/config.go b/src/models/server/config.go
index 58ae0da3..d7f06215 100644
--- a/src/models/server/config.go
+++ b/src/models/server/config.go
@@ -45,6 +45,8 @@ var (
 	LogMaxDays        int64  = 3
 	PrivilegeMode     bool   = false
 	PrivilegeToken    string = ""
+	CtrlConnTimeout   int64  = 10
+	Domain            string = ""
 
 	// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
 	PrivilegeAllowPorts map[int64]struct{}
@@ -222,6 +224,16 @@ func loadCommonConf(confFile string) error {
 			MaxPoolCount = v
 		}
 	}
+	tmpStr, ok = conf.Get("common", "conn_timeout")
+	if ok {
+		v, err := strconv.ParseInt(tmpStr, 10, 64)
+		if err != nil {
+			return fmt.Errorf("Parse conf error: conn_timeout is incorrect")
+		} else {
+			CtrlConnTimeout = v
+		}
+	}
+	Domain, ok = conf.Get("common", "domain")
 	return nil
 }
 
diff --git a/src/models/server/server.go b/src/models/server/server.go
index 2e2618d5..ec3fcad3 100644
--- a/src/models/server/server.go
+++ b/src/models/server/server.go
@@ -130,6 +130,12 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) {
 			}
 			p.listeners = append(p.listeners, l)
 		}
+		l, err := VhostHttpMuxer.Listen(p.SubDomain, p.HostHeaderRewrite, p.HttpUserName, p.HttpPassWord)
+		if err != nil {
+			return err
+		}
+		p.listeners = append(p.listeners, l)
+
 	} else if p.Type == "https" {
 		for _, domain := range p.CustomDomains {
 			l, err := VhostHttpsMuxer.Listen(domain, p.HostHeaderRewrite, p.HttpUserName, p.HttpPassWord)

From 54beb19435eee0200ce6ebe1ab0a1117cafb1669 Mon Sep 17 00:00:00 2001
From: Maodanping <673698750@qq.com>
Date: Sat, 5 Nov 2016 14:26:04 +0800
Subject: [PATCH 2/4] frps/control.go remove log info

---
 src/cmd/frps/control.go | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/cmd/frps/control.go b/src/cmd/frps/control.go
index 851ceee1..6dc43441 100644
--- a/src/cmd/frps/control.go
+++ b/src/cmd/frps/control.go
@@ -306,7 +306,6 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 			log.Warn(info)
 			return
 		}
-		log.Info("serverProxy: %+v", s)
 
 		// update metric's proxy status
 		metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip, s.PrivilegeMode, s.CustomDomains, s.ListenPort)

From 7cc5d03f351bfb9576ec7c173257d8c648cfeb8f Mon Sep 17 00:00:00 2001
From: Maodanping <673698750@qq.com>
Date: Mon, 7 Nov 2016 09:51:27 +0800
Subject: [PATCH 3/4] conf/frps.ini modify domain field

---
 conf/frps.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/conf/frps.ini b/conf/frps.ini
index 8835f634..e17ab11a 100644
--- a/conf/frps.ini
+++ b/conf/frps.ini
@@ -29,7 +29,7 @@ max_pool_count = 100
 # conn_timeout set the timeout interval (seconds) when the frpc connects frps
 conn_timeout = 10
 # domain for frps
-domain = codermao.com
+domain = frps.com
 
 # ssh is the proxy name, client will use this name and auth_token to connect to server
 [ssh]

From c70235566952d14e4a0816d6bdc058f2958938a2 Mon Sep 17 00:00:00 2001
From: Maodanping <673698750@qq.com>
Date: Tue, 8 Nov 2016 13:40:40 +0800
Subject: [PATCH 4/4] frps/control rename authTimeout; add judgement for
 subdomain

---
 conf/frps.ini               |  5 +++--
 src/cmd/frps/control.go     | 13 +++++++++----
 src/models/server/config.go |  8 ++++----
 src/models/server/server.go | 10 ++++++----
 4 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/conf/frps.ini b/conf/frps.ini
index e17ab11a..3a3b14a8 100644
--- a/conf/frps.ini
+++ b/conf/frps.ini
@@ -26,8 +26,9 @@ privilege_token = 12345678
 privilege_allow_ports = 2000-3000,3001,3003,4000-50000
 # pool_count in each proxy will change to max_pool_count if they exceed the maximum value
 max_pool_count = 100
-# conn_timeout set the timeout interval (seconds) when the frpc connects frps
-conn_timeout = 10
+# authentication_timeout means the timeout interval (minute units) when the frpc connects frps
+# if authentication_timeout set zero, the time is not verified 
+authentication_timeout = 15
 # domain for frps
 domain = frps.com
 
diff --git a/src/cmd/frps/control.go b/src/cmd/frps/control.go
index 6dc43441..db5c4087 100644
--- a/src/cmd/frps/control.go
+++ b/src/cmd/frps/control.go
@@ -18,6 +18,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"strings"
 	"time"
 
 	"github.com/fatedier/frp/src/models/consts"
@@ -221,8 +222,8 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 	nowTime := time.Now().Unix()
 	if req.PrivilegeMode {
 		privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeToken + fmt.Sprintf("%d", req.Timestamp))
-		// privilegeKey unavaiable after server.CtrlConnTimeout seconds
-		if server.CtrlConnTimeout != 0 && nowTime-req.Timestamp > server.CtrlConnTimeout {
+		// privilegeKey unavaiable after server.AuthTimeout minutes
+		if server.AuthTimeout != 0 && nowTime-req.Timestamp > server.AuthTimeout {
 			info = fmt.Sprintf("ProxyName [%s], privilege mode authorization timeout", req.ProxyName)
 			log.Warn(info)
 			return
@@ -234,8 +235,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 		}
 	} else {
 		authKey := pcrypto.GetAuthKey(req.ProxyName + s.AuthToken + fmt.Sprintf("%d", req.Timestamp))
-		// privilegeKey unavaiable after server.CtrlConnTimeout seconds
-		if server.CtrlConnTimeout != 0 && nowTime-req.Timestamp > server.CtrlConnTimeout {
+		if server.AuthTimeout != 0 && nowTime-req.Timestamp > server.AuthTimeout {
 			info = fmt.Sprintf("ProxyName [%s], authorization timeout", req.ProxyName)
 			log.Warn(info)
 			return
@@ -291,6 +291,11 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
 		s.HttpPassWord = req.HttpPassWord
 		// package URL
 		if req.SubDomain != "" {
+			if strings.Contains(req.SubDomain, ".") || strings.Contains(req.SubDomain, "*") {
+				info = fmt.Sprintf("ProxyName [%s], type [%s] not support when subdomain is not set", req.ProxyName, req.Type)
+				log.Warn(info)
+				return
+			}
 			s.SubDomain = req.SubDomain + "." + server.Domain
 		}
 		if req.PoolCount > server.MaxPoolCount {
diff --git a/src/models/server/config.go b/src/models/server/config.go
index d7f06215..c246c6a8 100644
--- a/src/models/server/config.go
+++ b/src/models/server/config.go
@@ -45,7 +45,7 @@ var (
 	LogMaxDays        int64  = 3
 	PrivilegeMode     bool   = false
 	PrivilegeToken    string = ""
-	CtrlConnTimeout   int64  = 10
+	AuthTimeout       int64  = 15
 	Domain            string = ""
 
 	// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
@@ -224,13 +224,13 @@ func loadCommonConf(confFile string) error {
 			MaxPoolCount = v
 		}
 	}
-	tmpStr, ok = conf.Get("common", "conn_timeout")
+	tmpStr, ok = conf.Get("common", "authentication_timeout")
 	if ok {
 		v, err := strconv.ParseInt(tmpStr, 10, 64)
 		if err != nil {
-			return fmt.Errorf("Parse conf error: conn_timeout is incorrect")
+			return fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
 		} else {
-			CtrlConnTimeout = v
+			AuthTimeout = v
 		}
 	}
 	Domain, ok = conf.Get("common", "domain")
diff --git a/src/models/server/server.go b/src/models/server/server.go
index ec3fcad3..9fb8db16 100644
--- a/src/models/server/server.go
+++ b/src/models/server/server.go
@@ -130,11 +130,13 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) {
 			}
 			p.listeners = append(p.listeners, l)
 		}
-		l, err := VhostHttpMuxer.Listen(p.SubDomain, p.HostHeaderRewrite, p.HttpUserName, p.HttpPassWord)
-		if err != nil {
-			return err
+		if p.SubDomain != "" {
+			l, err := VhostHttpMuxer.Listen(p.SubDomain, p.HostHeaderRewrite, p.HttpUserName, p.HttpPassWord)
+			if err != nil {
+				return err
+			}
+			p.listeners = append(p.listeners, l)
 		}
-		p.listeners = append(p.listeners, l)
 
 	} else if p.Type == "https" {
 		for _, domain := range p.CustomDomains {