From 817f4463f422ceda737feb0392aa3b69b0a23975 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 12 Jun 2016 21:57:55 +0800 Subject: [PATCH 1/9] cmd/frps: fix a bug when vhost_http_port is not set, fix #30 --- src/frp/cmd/frps/control.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index 0784955..16c3b3a 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -224,6 +224,13 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { return } + // check if vhost_port is set + if s.Type == "http" && server.VhostMuxer == nil { + info = fmt.Sprintf("ProxyName [%s], type [http] not support when vhost_http_port is not set", req.ProxyName) + log.Warn(info) + return + } + // set infomations from frpc s.UseEncryption = req.UseEncryption From f3876d69bb789d152ba6c50027f486eb561b097b Mon Sep 17 00:00:00 2001 From: Maodanping <673698750@qq.com> Date: Mon, 13 Jun 2016 22:19:24 +0800 Subject: [PATCH 2/9] add https proto for reverse proxy --- conf/frps.ini | 3 +- src/frp/cmd/frps/main.go | 15 +- src/frp/models/client/config.go | 2 +- src/frp/models/server/config.go | 31 ++++- src/frp/models/server/server.go | 10 +- src/frp/utils/vhost/vhost_https.go | 217 +++++++++++++++++++++++++++++ 6 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 src/frp/utils/vhost/vhost_https.go diff --git a/conf/frps.ini b/conf/frps.ini index e3087d8..e96fb17 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -4,6 +4,7 @@ bind_addr = 0.0.0.0 bind_port = 7000 # if you want to support virtual host, you must set the http port for listening (optional) vhost_http_port = 80 +vhost_https_port = 443 # if you want to configure or reload frps by dashboard, dashboard_port must be set dashboard_port = 7500 # console or real logFile path like ./frps.log @@ -20,7 +21,7 @@ bind_addr = 0.0.0.0 listen_port = 6000 [web01] -type = http +type = https auth_token = 123 # if proxy type equals http, custom_domains must be set separated by commas custom_domains = web01.yourdomain.com,web01.yourdomain2.com diff --git a/src/frp/cmd/frps/main.go b/src/frp/cmd/frps/main.go index 910827f..de7f7be 100644 --- a/src/frp/cmd/frps/main.go +++ b/src/frp/cmd/frps/main.go @@ -143,12 +143,25 @@ func main() { log.Error("Create vhost http listener error, %v", err) os.Exit(1) } - server.VhostMuxer, err = vhost.NewHttpMuxer(vhostListener, 30*time.Second) + server.VhostHttpMuxer, err = vhost.NewHttpMuxer(vhostListener, 30*time.Second) if err != nil { log.Error("Create vhost httpMuxer error, %v", err) } } + // create vhost if VhostHttpPort != 0 + if server.VhostHttpsPort != 0 { + vhostListener, err := conn.Listen(server.BindAddr, server.VhostHttpsPort) + if err != nil { + log.Error("Create vhost https listener error, %v", err) + os.Exit(1) + } + server.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(vhostListener, 30*time.Second) + if err != nil { + log.Error("Create vhost httpsMuxer error, %v", err) + } + } + // create dashboard web server if DashboardPort is set, so it won't be 0 if server.DashboardPort != 0 { err := server.RunDashboardServer(server.BindAddr, server.DashboardPort) diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 1d19c33..6bf1e40 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -115,7 +115,7 @@ func LoadConf(confFile string) (err error) { proxyClient.Type = "tcp" typeStr, ok := section["type"] if ok { - if typeStr != "tcp" && typeStr != "http" { + if typeStr != "tcp" && typeStr != "http" && typeStr != "https" { return fmt.Errorf("Parse ini file error: proxy [%s] type error", proxyClient.Name) } proxyClient.Type = typeStr diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 14b9753..4c76c66 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -32,6 +32,7 @@ var ( BindAddr string = "0.0.0.0" BindPort int64 = 7000 VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http + VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for http DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available LogFile string = "console" LogWay string = "console" // console or file @@ -40,7 +41,8 @@ var ( HeartBeatTimeout int64 = 90 UserConnTimeout int64 = 10 - VhostMuxer *vhost.HttpMuxer + VhostHttpMuxer *vhost.HttpMuxer + VhostHttpsMuxer *vhost.HttpsMuxer ProxyServers map[string]*ProxyServer = make(map[string]*ProxyServer) // all proxy servers info and resources ProxyServersMutex sync.RWMutex ) @@ -91,6 +93,14 @@ func loadCommonConf(confFile string) error { VhostHttpPort = 0 } + tmpStr, ok = conf.Get("common", "vhost_https_port") + if ok { + VhostHttpsPort, _ = strconv.ParseInt(tmpStr, 10, 64) + } else { + VhostHttpsPort = 0 + } + vhost.VhostHttpsPort = VhostHttpsPort + tmpStr, ok = conf.Get("common", "dashboard_port") if ok { DashboardPort, _ = strconv.ParseInt(tmpStr, 10, 64) @@ -135,7 +145,7 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e proxyServer.Type, ok = section["type"] if ok { - if proxyServer.Type != "tcp" && proxyServer.Type != "http" { + if proxyServer.Type != "tcp" && proxyServer.Type != "http" && proxyServer.Type != "https" { return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] type error", proxyServer.Name) } } else { @@ -179,6 +189,23 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + suffix } } + } else if proxyServer.Type == "https" { + // for https + domainStr, ok := section["custom_domains"] + if ok { + var suffix string + if VhostHttpsPort != 443 { + suffix = fmt.Sprintf(":%d", VhostHttpsPort) + } + proxyServer.CustomDomains = strings.Split(domainStr, ",") + if len(proxyServer.CustomDomains) == 0 { + return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyServer.Name) + } + for i, domain := range proxyServer.CustomDomains { + proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + suffix + } + log.Info("proxyServer: %+v", proxyServer.CustomDomains) + } } proxyServers[proxyServer.Name] = proxyServer } diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index b9affdf..d95e6eb 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -100,7 +100,15 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) { p.listeners = append(p.listeners, l) } else if p.Type == "http" { for _, domain := range p.CustomDomains { - l, err := VhostMuxer.Listen(domain) + l, err := VhostHttpMuxer.Listen(domain) + 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) if err != nil { return err } diff --git a/src/frp/utils/vhost/vhost_https.go b/src/frp/utils/vhost/vhost_https.go new file mode 100644 index 0000000..23d81ff --- /dev/null +++ b/src/frp/utils/vhost/vhost_https.go @@ -0,0 +1,217 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vhost + +import ( + _ "bufio" + _ "bytes" + _ "crypto/tls" + "errors" + "fmt" + "frp/utils/conn" + "frp/utils/log" + "io" + _ "io/ioutil" + "net" + _ "net/http" + "strings" + _ "sync" + "time" +) + +var ( + maxHandshake int64 = 65536 // maximum handshake we support (protocol max is 16 MB) + VhostHttpsPort int64 = 443 +) + +const ( + typeClientHello uint8 = 1 // Type client hello +) + +// TLS extension numbers +const ( + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionALPN uint16 = 16 + extensionSCT uint16 = 18 + extensionSessionTicket uint16 = 35 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionRenegotiationInfo uint16 = 0xff01 +) + +type HttpsMuxer struct { + *VhostMuxer +} + +/* + RFC document: http://tools.ietf.org/html/rfc5246 +*/ + +func errMsgToLog(format string, a ...interface{}) error { + errMsg := fmt.Sprintf(format, a...) + log.Warn(errMsg) + return errors.New(errMsg) +} + +func readHandshake(rd io.Reader) (string, error) { + + data := make([]byte, 1024) + length, err := rd.Read(data) + if err != nil { + return "", errMsgToLog("read err:%v", err) + } else { + if length < 47 { + return "", errMsgToLog("readHandshake: proto length[%d] is too short", length) + } + } + data = data[:length] + //log.Warn("data: %+v", data) + if uint8(data[5]) != typeClientHello { + return "", errMsgToLog("readHandshake: type[%d] is not clientHello", uint16(data[5])) + } + + //version and random + //tlsVersion := uint16(data[9])<<8 | uint16(data[10]) + //random := data[11:43] + + //session + sessionIdLen := int(data[43]) + if sessionIdLen > 32 || len(data) < 44+sessionIdLen { + return "", errMsgToLog("readHandshake: sessionIdLen[%d] is long", sessionIdLen) + } + data = data[44+sessionIdLen:] + if len(data) < 2 { + return "", errMsgToLog("readHandshake: dataLen[%d] after session is short", len(data)) + } + + // cipher suite numbers + cipherSuiteLen := int(data[0])<<8 | int(data[1]) + if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { + //return "", errMsgToLog("readHandshake: cipherSuiteLen[%d] is long", sessionIdLen) + return "", errMsgToLog("readHandshake: dataLen[%d] after cipher suite is short", len(data)) + } + data = data[2+cipherSuiteLen:] + if len(data) < 1 { + return "", errMsgToLog("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen) + } + + //compression method + compressionMethodsLen := int(data[0]) + if len(data) < 1+compressionMethodsLen { + return "", errMsgToLog("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen) + //return false + } + + data = data[1+compressionMethodsLen:] + + if len(data) == 0 { + // ClientHello is optionally followed by extension data + //return true + return "", errMsgToLog("readHandshake: there is no extension data to get servername") + } + if len(data) < 2 { + return "", errMsgToLog("readHandshake: extension dataLen[%d] is too short") + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extensionsLength != len(data) { + return "", errMsgToLog("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data)) + } + for len(data) != 0 { + if len(data) < 4 { + return "", errMsgToLog("readHandshake: extensionsDataLen[%d] is too short", len(data)) + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return "", errMsgToLog("readHandshake: extensionLen[%d] is long", length) + //return false + } + + switch extension { + case extensionRenegotiationInfo: + if length != 1 || data[0] != 0 { + return "", errMsgToLog("readHandshake: extension reNegotiationInfoLen[%d] is short", length) + } + case extensionNextProtoNeg: + case extensionStatusRequest: + case extensionServerName: + d := data[:length] + if len(d) < 2 { + return "", errMsgToLog("readHandshake: remiaining dataLen[%d] is short", len(d)) + } + namesLen := int(d[0])<<8 | int(d[1]) + d = d[2:] + if len(d) != namesLen { + return "", errMsgToLog("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d)) + } + for len(d) > 0 { + if len(d) < 3 { + return "", errMsgToLog("readHandshake: extension serverNameLen[%d] is short", len(d)) + } + nameType := d[0] + nameLen := int(d[1])<<8 | int(d[2]) + d = d[3:] + if len(d) < nameLen { + return "", errMsgToLog("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d)) + } + if nameType == 0 { + suffix := "" + if VhostHttpsPort != 443 { + suffix = fmt.Sprintf(":%d", VhostHttpsPort) + } + serverName := string(d[:nameLen]) + domain := strings.ToLower(strings.TrimSpace(serverName)) + suffix + return domain, nil + break + } + d = d[nameLen:] + } + } + data = data[length:] + } + //return "test.codermao.com:8082", nil + return "", errMsgToLog("Unknow error") +} + +func GetHttpsHostname(c *conn.Conn) (sc net.Conn, routerName string, err error) { + log.Info("GetHttpsHostname") + sc, rd := newShareConn(c.TcpConn) + + host, err := readHandshake(rd) + if err != nil { + return sc, "", err + } + /* + if _, ok := c.TcpConn.(*tls.Conn); ok { + log.Warn("convert to tlsConn success") + } else { + log.Warn("convert to tlsConn error") + }*/ + //tcpConn. + log.Debug("GetHttpsHostname: %s", host) + + return sc, host, nil +} + +func NewHttpsMuxer(listener *conn.Listener, timeout time.Duration) (*HttpsMuxer, error) { + mux, err := NewVhostMuxer(listener, GetHttpsHostname, timeout) + return &HttpsMuxer{mux}, err +} From ab6c5c813efc4e90c7690bc438db70e2601149e0 Mon Sep 17 00:00:00 2001 From: Gogs Date: Tue, 14 Jun 2016 16:58:53 +0800 Subject: [PATCH 3/9] User can set use aes or gzip --- src/frp/models/client/client.go | 8 +-- src/frp/models/client/config.go | 11 +++- src/frp/models/msg/msg.go | 2 +- src/frp/models/server/server.go | 8 +-- src/frp/utils/conn/conn.go | 59 ++++++++++++++---- src/frp/utils/pcrypto/pcrypto.go | 90 +++++++++++++++------------ src/frp/utils/pcrypto/pcrypto_test.go | 78 ++++++++++++++++++----- 7 files changed, 173 insertions(+), 83 deletions(-) diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index 6165eee..edb7671 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -32,7 +32,7 @@ type ProxyClient struct { LocalIp string LocalPort int64 Type string - UseEncryption bool + UseEncryption int } func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { @@ -89,11 +89,7 @@ func (p *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err erro // l means local, r means remote log.Debug("Join two connections, (l[%s] r[%s]) (l[%s] r[%s])", localConn.GetLocalAddr(), localConn.GetRemoteAddr(), remoteConn.GetLocalAddr(), remoteConn.GetRemoteAddr()) - if p.UseEncryption { - go conn.JoinMore(localConn, remoteConn, p.AuthToken) - } else { - go conn.Join(localConn, remoteConn) - } + go conn.JoinMore(localConn, remoteConn, p.AuthToken, p.UseEncryption) return nil } diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 1d19c33..a12ffde 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -122,10 +122,15 @@ func LoadConf(confFile string) (err error) { } // use_encryption - proxyClient.UseEncryption = false + proxyClient.UseEncryption = 0 useEncryptionStr, ok := section["use_encryption"] - if ok && useEncryptionStr == "true" { - proxyClient.UseEncryption = true + if ok { + tmpRes, err := strconv.Atoi(useEncryptionStr) + if err != nil { + proxyClient.UseEncryption = 0 + } + + proxyClient.UseEncryption = tmpRes } ProxyClients[proxyClient.Name] = proxyClient diff --git a/src/frp/models/msg/msg.go b/src/frp/models/msg/msg.go index d1b57ad..d88f8ae 100644 --- a/src/frp/models/msg/msg.go +++ b/src/frp/models/msg/msg.go @@ -24,7 +24,7 @@ type ControlReq struct { Type int64 `json:"type"` ProxyName string `json:"proxy_name,omitempty"` AuthKey string `json:"auth_key, omitempty"` - UseEncryption bool `json:"use_encryption, omitempty"` + UseEncryption int `json:"use_encryption, omitempty"` Timestamp int64 `json:"timestamp, omitempty"` } diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index b9affdf..ae9ff02 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -38,7 +38,7 @@ type ProxyServer struct { CustomDomains []string // configure in frpc.ini - UseEncryption bool + UseEncryption int Status int64 CtlConn *conn.Conn // control connection with frpc @@ -144,11 +144,7 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) { log.Debug("Join two connections, (l[%s] r[%s]) (l[%s] r[%s])", workConn.GetLocalAddr(), workConn.GetRemoteAddr(), userConn.GetLocalAddr(), userConn.GetRemoteAddr()) - if p.UseEncryption { - go conn.JoinMore(userConn, workConn, p.AuthToken) - } else { - go conn.Join(userConn, workConn) - } + go conn.JoinMore(userConn, workConn, p.AuthToken, p.UseEncryption) }() } }(listener) diff --git a/src/frp/utils/conn/conn.go b/src/frp/utils/conn/conn.go index 1c6eeb1..c41bc2a 100644 --- a/src/frp/utils/conn/conn.go +++ b/src/frp/utils/conn/conn.go @@ -16,6 +16,8 @@ package conn import ( "bufio" + "bytes" + "encoding/binary" "fmt" "io" "net" @@ -192,48 +194,70 @@ func Join(c1 *Conn, c2 *Conn) { // messages from c1 to c2 will be encrypted // and from c2 to c1 will be decrypted -func JoinMore(c1 *Conn, c2 *Conn, cryptKey string) { +func JoinMore(c1 *Conn, c2 *Conn, cryptKey string, ptype int) { var wait sync.WaitGroup - encryptPipe := func(from *Conn, to *Conn, key string) { + encryptPipe := func(from *Conn, to *Conn, key string, ttype int) { defer from.Close() defer to.Close() defer wait.Done() // we don't care about errors here - PipeEncrypt(from.TcpConn, to.TcpConn, key) + PipeEncrypt(from.TcpConn, to.TcpConn, key, ttype) } - decryptPipe := func(to *Conn, from *Conn, key string) { + decryptPipe := func(to *Conn, from *Conn, key string, ttype int) { defer from.Close() defer to.Close() defer wait.Done() // we don't care about errors here - PipeDecrypt(to.TcpConn, from.TcpConn, key) + PipeDecrypt(to.TcpConn, from.TcpConn, key, ttype) } wait.Add(2) - go encryptPipe(c1, c2, cryptKey) - go decryptPipe(c2, c1, cryptKey) + go encryptPipe(c1, c2, cryptKey, ptype) + + go decryptPipe(c2, c1, cryptKey, ptype) wait.Wait() log.Debug("One tunnel stopped") return } +func unpkgMsg(data []byte) (int, []byte, []byte) { + if len(data) < 4 { + return -1, nil, nil + } + llen := int(binary.BigEndian.Uint32(data[0:4])) + // no complete + if len(data) < llen+4 { + return -1, nil, nil + } + + return 0, data[4 : llen+4], data[llen+4:] +} + // decrypt msg from reader, then write into writer -func PipeDecrypt(r net.Conn, w net.Conn, key string) error { +func PipeDecrypt(r net.Conn, w net.Conn, key string, ptype int) error { laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(key)); err != nil { + if err := laes.Init([]byte(key), ptype); err != nil { log.Error("Pcrypto Init error: %v", err) return fmt.Errorf("Pcrypto Init error: %v", err) } + buf := make([]byte, 10*1024) + var left []byte nreader := bufio.NewReader(r) for { - buf, err := nreader.ReadBytes('\n') + n, err := nreader.Read(buf) if err != nil { return err } + left := append(left, buf[:n]...) + cnt, buf, left := unpkgMsg(left) + + if cnt < 0 { + continue + } res, err := laes.Decrypt(buf) if err != nil { @@ -249,10 +273,18 @@ func PipeDecrypt(r net.Conn, w net.Conn, key string) error { return nil } +func pkgMsg(data []byte) []byte { + llen := uint32(len(data)) + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, llen) + buf.Write(data) + return buf.Bytes() +} + // recvive msg from reader, then encrypt msg into write -func PipeEncrypt(r net.Conn, w net.Conn, key string) error { +func PipeEncrypt(r net.Conn, w net.Conn, key string, ptype int) error { laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(key)); err != nil { + if err := laes.Init([]byte(key), ptype); err != nil { log.Error("Pcrypto Init error: %v", err) return fmt.Errorf("Pcrypto Init error: %v", err) } @@ -271,11 +303,12 @@ func PipeEncrypt(r net.Conn, w net.Conn, key string) error { return fmt.Errorf("Encrypt error: %v", err) } - res = append(res, '\n') + res = pkgMsg(res) _, err = w.Write(res) if err != nil { return err } } + return nil } diff --git a/src/frp/utils/pcrypto/pcrypto.go b/src/frp/utils/pcrypto/pcrypto.go index a4772a8..6c646d1 100644 --- a/src/frp/utils/pcrypto/pcrypto.go +++ b/src/frp/utils/pcrypto/pcrypto.go @@ -20,7 +20,6 @@ import ( "crypto/aes" "crypto/cipher" "crypto/md5" - "encoding/base64" "encoding/hex" "errors" "fmt" @@ -30,69 +29,80 @@ import ( type Pcrypto struct { pkey []byte paes cipher.Block + // 0: nono; 1:compress; 2: encrypt; 3: compress and encrypt + ptyp int } -func (pc *Pcrypto) Init(key []byte) error { +func (pc *Pcrypto) Init(key []byte, ptyp int) error { var err error pc.pkey = pKCS7Padding(key, aes.BlockSize) pc.paes, err = aes.NewCipher(pc.pkey) + if ptyp == 1 || ptyp == 2 || ptyp == 3 { + pc.ptyp = ptyp + } else { + pc.ptyp = 0 + } return err } func (pc *Pcrypto) Encrypt(src []byte) ([]byte, error) { - // gzip var zbuf bytes.Buffer - zwr, err := gzip.NewWriterLevel(&zbuf, -1) - if err != nil { - return nil, err + + // gzip + if pc.ptyp == 1 || pc.ptyp == 3 { + zwr, err := gzip.NewWriterLevel(&zbuf, gzip.DefaultCompression) + if err != nil { + return nil, err + } + defer zwr.Close() + zwr.Write(src) + zwr.Flush() + src = zbuf.Bytes() } - defer zwr.Close() - zwr.Write(src) - zwr.Flush() // aes - src = pKCS7Padding(zbuf.Bytes(), aes.BlockSize) - blockMode := cipher.NewCBCEncrypter(pc.paes, pc.pkey) - crypted := make([]byte, len(src)) - blockMode.CryptBlocks(crypted, src) + if pc.ptyp == 2 || pc.ptyp == 3 { + src = pKCS7Padding(src, aes.BlockSize) + blockMode := cipher.NewCBCEncrypter(pc.paes, pc.pkey) + crypted := make([]byte, len(src)) + blockMode.CryptBlocks(crypted, src) + src = crypted + } - // base64 - return []byte(base64.StdEncoding.EncodeToString(crypted)), nil + return src, nil } func (pc *Pcrypto) Decrypt(str []byte) ([]byte, error) { - // base64 - data, err := base64.StdEncoding.DecodeString(string(str)) - if err != nil { - return nil, err - } - // aes - decryptText, err := hex.DecodeString(fmt.Sprintf("%x", data)) - if err != nil { - return nil, err + if pc.ptyp == 2 || pc.ptyp == 3 { + decryptText, err := hex.DecodeString(fmt.Sprintf("%x", str)) + if err != nil { + return nil, err + } + + if len(decryptText)%aes.BlockSize != 0 { + return nil, errors.New("crypto/cipher: ciphertext is not a multiple of the block size") + } + + blockMode := cipher.NewCBCDecrypter(pc.paes, pc.pkey) + + blockMode.CryptBlocks(decryptText, decryptText) + str = pKCS7UnPadding(decryptText) } - if len(decryptText)%aes.BlockSize != 0 { - return nil, errors.New("crypto/cipher: ciphertext is not a multiple of the block size") - } - - blockMode := cipher.NewCBCDecrypter(pc.paes, pc.pkey) - - blockMode.CryptBlocks(decryptText, decryptText) - decryptText = pKCS7UnPadding(decryptText) - // gunzip - zbuf := bytes.NewBuffer(decryptText) - zrd, err := gzip.NewReader(zbuf) - if err != nil { - return nil, err + if pc.ptyp == 1 || pc.ptyp == 3 { + zbuf := bytes.NewBuffer(str) + zrd, err := gzip.NewReader(zbuf) + if err != nil { + return nil, err + } + defer zrd.Close() + str, _ = ioutil.ReadAll(zrd) } - defer zrd.Close() - data, _ = ioutil.ReadAll(zrd) - return data, nil + return str, nil } func pKCS7Padding(ciphertext []byte, blockSize int) []byte { diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index e86762f..e240ba3 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -20,28 +20,78 @@ import ( ) func TestEncrypt(t *testing.T) { + return pp := new(Pcrypto) - pp.Init([]byte("Hana")) - res, err := pp.Encrypt([]byte("Just One Test!")) + pp.Init([]byte("Hana"), 1) + res, err := pp.Encrypt([]byte("Test Encrypt!")) if err != nil { t.Fatal(err) } - fmt.Printf("[%x]\n", res) + fmt.Printf("Encrypt: len %d, [%x]\n", len(res), res) } func TestDecrypt(t *testing.T) { - pp := new(Pcrypto) - pp.Init([]byte("Hana")) - res, err := pp.Encrypt([]byte("Just One Test!")) - if err != nil { - t.Fatal(err) + fmt.Println("*****************************************************") + { + pp := new(Pcrypto) + pp.Init([]byte("Hana"), 0) + res, err := pp.Encrypt([]byte("Test Decrypt! 0")) + if err != nil { + t.Fatal(err) + } + + res, err = pp.Decrypt(res) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("[%s]\n", string(res)) + } + { + pp := new(Pcrypto) + pp.Init([]byte("Hana"), 1) + res, err := pp.Encrypt([]byte("Test Decrypt! 1")) + if err != nil { + t.Fatal(err) + } + + res, err = pp.Decrypt(res) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("[%s]\n", string(res)) + } + { + pp := new(Pcrypto) + pp.Init([]byte("Hana"), 2) + res, err := pp.Encrypt([]byte("Test Decrypt! 2")) + if err != nil { + t.Fatal(err) + } + + res, err = pp.Decrypt(res) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("[%s]\n", string(res)) + } + { + pp := new(Pcrypto) + pp.Init([]byte("Hana"), 3) + res, err := pp.Encrypt([]byte("Test Decrypt! 3")) + if err != nil { + t.Fatal(err) + } + + res, err = pp.Decrypt(res) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("[%s]\n", string(res)) } - res, err = pp.Decrypt(res) - if err != nil { - t.Fatal(err) - } - - fmt.Printf("[%s]\n", string(res)) } From c5e4b24f8f0979992707e9f26f2f0515c2461a8f Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 24 Jun 2016 13:12:34 +0800 Subject: [PATCH 4/9] all: add use_gzip configure and some improvements, see #28 --- conf/frpc.ini | 4 +- src/frp/cmd/frpc/control.go | 1 + src/frp/cmd/frps/control.go | 1 + src/frp/models/client/client.go | 12 +- src/frp/models/client/config.go | 16 +- src/frp/models/config/config.go | 23 +++ src/frp/models/msg/msg.go | 5 +- src/frp/models/msg/process.go | 202 ++++++++++++++++++++++++++ src/frp/models/server/config.go | 10 +- src/frp/models/server/server.go | 17 +-- src/frp/utils/conn/conn.go | 148 ------------------- src/frp/utils/pcrypto/pcrypto.go | 92 +++++------- src/frp/utils/pcrypto/pcrypto_test.go | 101 +++++-------- test/conf/auto_test_frpc.ini | 2 + test/run_test.sh | 6 +- 15 files changed, 337 insertions(+), 303 deletions(-) create mode 100644 src/frp/models/config/config.go create mode 100644 src/frp/models/msg/process.go diff --git a/conf/frpc.ini b/conf/frpc.ini index 9a1d8bc..38101d1 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -18,13 +18,15 @@ local_ip = 127.0.0.1 local_port = 22 # true or false, if true, messages between frps and frpc will be encrypted, default is false use_encryption = true +# default is false +use_gzip = false # Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02, the domains are set in frps.ini [web01] type = http local_ip = 127.0.0.1 local_port = 80 -use_encryption = true +use_gzip = true [web02] type = http diff --git a/src/frp/cmd/frpc/control.go b/src/frp/cmd/frpc/control.go index 53a936a..57ec52f 100644 --- a/src/frp/cmd/frpc/control.go +++ b/src/frp/cmd/frpc/control.go @@ -143,6 +143,7 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { ProxyName: cli.Name, AuthKey: authKey, UseEncryption: cli.UseEncryption, + UseGzip: cli.UseGzip, Timestamp: nowTime, } buf, _ := json.Marshal(req) diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index 0784955..7a9be92 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -226,6 +226,7 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { // set infomations from frpc s.UseEncryption = req.UseEncryption + s.UseGzip = req.UseGzip // start proxy and listen for user connections, no block err := s.Start(c) diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index edb7671..95c5931 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -19,6 +19,7 @@ import ( "fmt" "time" + "frp/models/config" "frp/models/consts" "frp/models/msg" "frp/utils/conn" @@ -27,12 +28,9 @@ import ( ) type ProxyClient struct { - Name string - AuthToken string - LocalIp string - LocalPort int64 - Type string - UseEncryption int + config.BaseConf + LocalIp string + LocalPort int64 } func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { @@ -89,7 +87,7 @@ func (p *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err erro // l means local, r means remote log.Debug("Join two connections, (l[%s] r[%s]) (l[%s] r[%s])", localConn.GetLocalAddr(), localConn.GetRemoteAddr(), remoteConn.GetLocalAddr(), remoteConn.GetRemoteAddr()) - go conn.JoinMore(localConn, remoteConn, p.AuthToken, p.UseEncryption) + go msg.JoinMore(localConn, remoteConn, p.BaseConf) return nil } diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index a12ffde..393b531 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -122,15 +122,17 @@ func LoadConf(confFile string) (err error) { } // use_encryption - proxyClient.UseEncryption = 0 + proxyClient.UseEncryption = false useEncryptionStr, ok := section["use_encryption"] - if ok { - tmpRes, err := strconv.Atoi(useEncryptionStr) - if err != nil { - proxyClient.UseEncryption = 0 - } + if ok && useEncryptionStr == "true" { + proxyClient.UseEncryption = true + } - proxyClient.UseEncryption = tmpRes + // use_gzip + proxyClient.UseGzip = false + useGzipStr, ok := section["use_gzip"] + if ok && useGzipStr == "true" { + proxyClient.UseGzip = true } ProxyClients[proxyClient.Name] = proxyClient diff --git a/src/frp/models/config/config.go b/src/frp/models/config/config.go new file mode 100644 index 0000000..3ef4889 --- /dev/null +++ b/src/frp/models/config/config.go @@ -0,0 +1,23 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +type BaseConf struct { + Name string + AuthToken string + Type string + UseEncryption bool + UseGzip bool +} diff --git a/src/frp/models/msg/msg.go b/src/frp/models/msg/msg.go index d88f8ae..e3ea2b2 100644 --- a/src/frp/models/msg/msg.go +++ b/src/frp/models/msg/msg.go @@ -19,12 +19,13 @@ type GeneralRes struct { Msg string `json:"msg"` } -// messages between control connection of frpc and frps +// messages between control connections of frpc and frps type ControlReq struct { Type int64 `json:"type"` ProxyName string `json:"proxy_name,omitempty"` AuthKey string `json:"auth_key, omitempty"` - UseEncryption int `json:"use_encryption, omitempty"` + UseEncryption bool `json:"use_encryption, omitempty"` + UseGzip bool `json:"use_gzip, omitempty"` Timestamp int64 `json:"timestamp, omitempty"` } diff --git a/src/frp/models/msg/process.go b/src/frp/models/msg/process.go new file mode 100644 index 0000000..b845269 --- /dev/null +++ b/src/frp/models/msg/process.go @@ -0,0 +1,202 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package msg + +import ( + "bufio" + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "sync" + + "frp/models/config" + "frp/utils/conn" + "frp/utils/log" + "frp/utils/pcrypto" +) + +// will block until connection close +func Join(c1 *conn.Conn, c2 *conn.Conn) { + var wait sync.WaitGroup + pipe := func(to *conn.Conn, from *conn.Conn) { + defer to.Close() + defer from.Close() + defer wait.Done() + + var err error + _, err = io.Copy(to.TcpConn, from.TcpConn) + if err != nil { + log.Warn("join connections error, %v", err) + } + } + + wait.Add(2) + go pipe(c1, c2) + go pipe(c2, c1) + wait.Wait() + return +} + +// join two connections and do some operations +func JoinMore(c1 *conn.Conn, c2 *conn.Conn, conf config.BaseConf) { + var wait sync.WaitGroup + encryptPipe := func(from *conn.Conn, to *conn.Conn) { + defer from.Close() + defer to.Close() + defer wait.Done() + + // we don't care about errors here + pipeEncrypt(from.TcpConn, to.TcpConn, conf) + } + + decryptPipe := func(to *conn.Conn, from *conn.Conn) { + defer from.Close() + defer to.Close() + defer wait.Done() + + // we don't care about errors here + pipeDecrypt(to.TcpConn, from.TcpConn, conf) + } + + wait.Add(2) + go encryptPipe(c1, c2) + go decryptPipe(c2, c1) + wait.Wait() + log.Debug("ProxyName [%s], One tunnel stopped", conf.Name) + return +} + +func pkgMsg(data []byte) []byte { + llen := uint32(len(data)) + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, llen) + buf.Write(data) + return buf.Bytes() +} + +func unpkgMsg(data []byte) (int, []byte, []byte) { + if len(data) < 4 { + return -1, nil, data + } + llen := int(binary.BigEndian.Uint32(data[0:4])) + // no complete + if len(data) < llen+4 { + return -1, nil, data + } + + return 0, data[4 : llen+4], data[llen+4:] +} + +// decrypt msg from reader, then write into writer +func pipeDecrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { + laes := new(pcrypto.Pcrypto) + if err := laes.Init([]byte(conf.AuthToken)); err != nil { + log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) + return fmt.Errorf("Pcrypto Init error: %v", err) + } + + buf := make([]byte, 5*1024+4) + var left, res []byte + var cnt int + nreader := bufio.NewReader(r) + for { + // there may be more than 1 package in variable + // and we read more bytes if unpkgMsg returns an error + var newBuf []byte + if cnt < 0 { + n, err := nreader.Read(buf) + if err != nil { + return err + } + newBuf = append(left, buf[0:n]...) + } else { + newBuf = left + } + cnt, res, left = unpkgMsg(newBuf) + if cnt < 0 { + continue + } + + // aes + if conf.UseEncryption { + res, err = laes.Decrypt(res) + if err != nil { + log.Warn("ProxyName [%s], decrypt error, %v", conf.Name, err) + return fmt.Errorf("Decrypt error: %v", err) + } + } + // gzip + if conf.UseGzip { + log.Warn("%x", res) + res, err = laes.Decompression(res) + if err != nil { + log.Warn("ProxyName [%s], decompression error, %v", conf.Name, err) + return fmt.Errorf("Decompression error: %v", err) + } + } + + _, err = w.Write(res) + if err != nil { + return err + } + } + return nil +} + +// recvive msg from reader, then encrypt msg into writer +func pipeEncrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { + laes := new(pcrypto.Pcrypto) + if err := laes.Init([]byte(conf.AuthToken)); err != nil { + log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) + return fmt.Errorf("Pcrypto Init error: %v", err) + } + + nreader := bufio.NewReader(r) + buf := make([]byte, 5*1024) + for { + n, err := nreader.Read(buf) + if err != nil { + return err + } + + res := buf[0:n] + // gzip + if conf.UseGzip { + res, err = laes.Compression(res) + if err != nil { + log.Warn("ProxyName [%s], compression error: %v", conf.Name, err) + return fmt.Errorf("Compression error: %v", err) + } + } + // aes + if conf.UseEncryption { + res, err = laes.Encrypt(res) + if err != nil { + log.Warn("ProxyName [%s], encrypt error: %v", conf.Name, err) + return fmt.Errorf("Encrypt error: %v", err) + } + } + + res = pkgMsg(res) + _, err = w.Write(res) + if err != nil { + return err + } + } + + return nil +} diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 14b9753..82f4ce2 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -81,7 +81,10 @@ func loadCommonConf(confFile string) error { tmpStr, ok = conf.Get("common", "bind_port") if ok { - BindPort, _ = strconv.ParseInt(tmpStr, 10, 64) + v, err := strconv.ParseInt(tmpStr, 10, 64) + if err == nil { + BindPort = v + } } tmpStr, ok = conf.Get("common", "vhost_http_port") @@ -115,7 +118,10 @@ func loadCommonConf(confFile string) error { tmpStr, ok = conf.Get("common", "log_max_days") if ok { - LogMaxDays, _ = strconv.ParseInt(tmpStr, 10, 64) + v, err := strconv.ParseInt(tmpStr, 10, 64) + if err == nil { + LogMaxDays = v + } } return nil } diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index ae9ff02..342343d 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -19,7 +19,9 @@ import ( "sync" "time" + "frp/models/config" "frp/models/consts" + "frp/models/msg" "frp/utils/conn" "frp/utils/log" ) @@ -30,16 +32,11 @@ type Listener interface { } type ProxyServer struct { - Name string - AuthToken string - Type string + config.BaseConf BindAddr string ListenPort int64 CustomDomains []string - // configure in frpc.ini - UseEncryption int - Status int64 CtlConn *conn.Conn // control connection with frpc listeners []Listener // accept new connection from remote users @@ -144,7 +141,7 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) { log.Debug("Join two connections, (l[%s] r[%s]) (l[%s] r[%s])", workConn.GetLocalAddr(), workConn.GetRemoteAddr(), userConn.GetLocalAddr(), userConn.GetRemoteAddr()) - go conn.JoinMore(userConn, workConn, p.AuthToken, p.UseEncryption) + go msg.JoinMore(userConn, workConn, p.BaseConf) }() } }(listener) @@ -186,9 +183,9 @@ func (p *ProxyServer) RegisterNewWorkConn(c *conn.Conn) { p.workConnChan <- c } -// when frps get one user connection, we get one work connection from the pool and return it -// if no workConn available in the pool, send message to frpc to get one or more -// and wait until it is available +// When frps get one user connection, we get one work connection from the pool and return it. +// If no workConn available in the pool, send message to frpc to get one or more +// and wait until it is available. // return an error if wait timeout func (p *ProxyServer) getWorkConn() (workConn *conn.Conn, err error) { var ok bool diff --git a/src/frp/utils/conn/conn.go b/src/frp/utils/conn/conn.go index c41bc2a..ed330f6 100644 --- a/src/frp/utils/conn/conn.go +++ b/src/frp/utils/conn/conn.go @@ -16,17 +16,12 @@ package conn import ( "bufio" - "bytes" - "encoding/binary" "fmt" "io" "net" "strings" "sync" "time" - - "frp/utils/log" - "frp/utils/pcrypto" ) type Listener struct { @@ -169,146 +164,3 @@ func (c *Conn) IsClosed() (closeFlag bool) { c.mutex.RUnlock() return } - -// will block until connection close -func Join(c1 *Conn, c2 *Conn) { - var wait sync.WaitGroup - pipe := func(to *Conn, from *Conn) { - defer to.Close() - defer from.Close() - defer wait.Done() - - var err error - _, err = io.Copy(to.TcpConn, from.TcpConn) - if err != nil { - log.Warn("join connections error, %v", err) - } - } - - wait.Add(2) - go pipe(c1, c2) - go pipe(c2, c1) - wait.Wait() - return -} - -// messages from c1 to c2 will be encrypted -// and from c2 to c1 will be decrypted -func JoinMore(c1 *Conn, c2 *Conn, cryptKey string, ptype int) { - var wait sync.WaitGroup - encryptPipe := func(from *Conn, to *Conn, key string, ttype int) { - defer from.Close() - defer to.Close() - defer wait.Done() - - // we don't care about errors here - PipeEncrypt(from.TcpConn, to.TcpConn, key, ttype) - } - - decryptPipe := func(to *Conn, from *Conn, key string, ttype int) { - defer from.Close() - defer to.Close() - defer wait.Done() - - // we don't care about errors here - PipeDecrypt(to.TcpConn, from.TcpConn, key, ttype) - } - - wait.Add(2) - go encryptPipe(c1, c2, cryptKey, ptype) - - go decryptPipe(c2, c1, cryptKey, ptype) - wait.Wait() - log.Debug("One tunnel stopped") - return -} - -func unpkgMsg(data []byte) (int, []byte, []byte) { - if len(data) < 4 { - return -1, nil, nil - } - llen := int(binary.BigEndian.Uint32(data[0:4])) - // no complete - if len(data) < llen+4 { - return -1, nil, nil - } - - return 0, data[4 : llen+4], data[llen+4:] -} - -// decrypt msg from reader, then write into writer -func PipeDecrypt(r net.Conn, w net.Conn, key string, ptype int) error { - laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(key), ptype); err != nil { - log.Error("Pcrypto Init error: %v", err) - return fmt.Errorf("Pcrypto Init error: %v", err) - } - - buf := make([]byte, 10*1024) - var left []byte - nreader := bufio.NewReader(r) - for { - n, err := nreader.Read(buf) - if err != nil { - return err - } - left := append(left, buf[:n]...) - cnt, buf, left := unpkgMsg(left) - - if cnt < 0 { - continue - } - - res, err := laes.Decrypt(buf) - if err != nil { - log.Error("Decrypt [%s] error, %v", string(buf), err) - return fmt.Errorf("Decrypt [%s] error: %v", string(buf), err) - } - - _, err = w.Write(res) - if err != nil { - return err - } - } - return nil -} - -func pkgMsg(data []byte) []byte { - llen := uint32(len(data)) - buf := new(bytes.Buffer) - binary.Write(buf, binary.BigEndian, llen) - buf.Write(data) - return buf.Bytes() -} - -// recvive msg from reader, then encrypt msg into write -func PipeEncrypt(r net.Conn, w net.Conn, key string, ptype int) error { - laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(key), ptype); err != nil { - log.Error("Pcrypto Init error: %v", err) - return fmt.Errorf("Pcrypto Init error: %v", err) - } - - nreader := bufio.NewReader(r) - buf := make([]byte, 10*1024) - - for { - n, err := nreader.Read(buf) - if err != nil { - return err - } - res, err := laes.Encrypt(buf[:n]) - if err != nil { - log.Error("Encrypt error: %v", err) - return fmt.Errorf("Encrypt error: %v", err) - } - - res = pkgMsg(res) - _, err = w.Write(res) - if err != nil { - return err - } - } - - return nil -} diff --git a/src/frp/utils/pcrypto/pcrypto.go b/src/frp/utils/pcrypto/pcrypto.go index 6c646d1..8f5e873 100644 --- a/src/frp/utils/pcrypto/pcrypto.go +++ b/src/frp/utils/pcrypto/pcrypto.go @@ -29,79 +29,61 @@ import ( type Pcrypto struct { pkey []byte paes cipher.Block - // 0: nono; 1:compress; 2: encrypt; 3: compress and encrypt - ptyp int } -func (pc *Pcrypto) Init(key []byte, ptyp int) error { +func (pc *Pcrypto) Init(key []byte) error { var err error pc.pkey = pKCS7Padding(key, aes.BlockSize) pc.paes, err = aes.NewCipher(pc.pkey) - if ptyp == 1 || ptyp == 2 || ptyp == 3 { - pc.ptyp = ptyp - } else { - pc.ptyp = 0 - } - return err } func (pc *Pcrypto) Encrypt(src []byte) ([]byte, error) { - var zbuf bytes.Buffer - - // gzip - if pc.ptyp == 1 || pc.ptyp == 3 { - zwr, err := gzip.NewWriterLevel(&zbuf, gzip.DefaultCompression) - if err != nil { - return nil, err - } - defer zwr.Close() - zwr.Write(src) - zwr.Flush() - src = zbuf.Bytes() - } - // aes - if pc.ptyp == 2 || pc.ptyp == 3 { - src = pKCS7Padding(src, aes.BlockSize) - blockMode := cipher.NewCBCEncrypter(pc.paes, pc.pkey) - crypted := make([]byte, len(src)) - blockMode.CryptBlocks(crypted, src) - src = crypted - } - - return src, nil + src = pKCS7Padding(src, aes.BlockSize) + blockMode := cipher.NewCBCEncrypter(pc.paes, pc.pkey) + crypted := make([]byte, len(src)) + blockMode.CryptBlocks(crypted, src) + return crypted, nil } func (pc *Pcrypto) Decrypt(str []byte) ([]byte, error) { // aes - if pc.ptyp == 2 || pc.ptyp == 3 { - decryptText, err := hex.DecodeString(fmt.Sprintf("%x", str)) - if err != nil { - return nil, err - } - - if len(decryptText)%aes.BlockSize != 0 { - return nil, errors.New("crypto/cipher: ciphertext is not a multiple of the block size") - } - - blockMode := cipher.NewCBCDecrypter(pc.paes, pc.pkey) - - blockMode.CryptBlocks(decryptText, decryptText) - str = pKCS7UnPadding(decryptText) + decryptText, err := hex.DecodeString(fmt.Sprintf("%x", str)) + if err != nil { + return nil, err } - // gunzip - if pc.ptyp == 1 || pc.ptyp == 3 { - zbuf := bytes.NewBuffer(str) - zrd, err := gzip.NewReader(zbuf) - if err != nil { - return nil, err - } - defer zrd.Close() - str, _ = ioutil.ReadAll(zrd) + if len(decryptText)%aes.BlockSize != 0 { + return nil, errors.New("crypto/cipher: ciphertext is not a multiple of the block size") } + blockMode := cipher.NewCBCDecrypter(pc.paes, pc.pkey) + + blockMode.CryptBlocks(decryptText, decryptText) + return pKCS7UnPadding(decryptText), nil +} + +func (pc *Pcrypto) Compression(src []byte) ([]byte, error) { + var zbuf bytes.Buffer + zwr, err := gzip.NewWriterLevel(&zbuf, gzip.DefaultCompression) + if err != nil { + return nil, err + } + defer zwr.Close() + zwr.Write(src) + zwr.Flush() + return zbuf.Bytes(), nil +} + +func (pc *Pcrypto) Decompression(src []byte) ([]byte, error) { + zbuf := bytes.NewBuffer(src) + zrd, err := gzip.NewReader(zbuf) + if err != nil { + return nil, err + } + defer zrd.Close() + str, _ := ioutil.ReadAll(zrd) return str, nil } diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index e240ba3..41a1755 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -15,83 +15,48 @@ package pcrypto import ( - "fmt" "testing" ) +var ( + pp *Pcrypto +) + +func init() { + pp = &Pcrypto{} + pp.Init([]byte("Hana")) +} + func TestEncrypt(t *testing.T) { - return - pp := new(Pcrypto) - pp.Init([]byte("Hana"), 1) - res, err := pp.Encrypt([]byte("Test Encrypt!")) + testStr := "Test Encrypt!" + res, err := pp.Encrypt([]byte(testStr)) if err != nil { - t.Fatal(err) + t.Fatalf("encrypt error: %v", err) } - fmt.Printf("Encrypt: len %d, [%x]\n", len(res), res) + res, err = pp.Decrypt([]byte(res)) + if err != nil { + t.Fatalf("decrypt error: %v", err) + } + + if string(res) != testStr { + t.Fatalf("test encrypt error, from [%s] to [%s]", testStr, string(res)) + } } -func TestDecrypt(t *testing.T) { - fmt.Println("*****************************************************") - { - pp := new(Pcrypto) - pp.Init([]byte("Hana"), 0) - res, err := pp.Encrypt([]byte("Test Decrypt! 0")) - if err != nil { - t.Fatal(err) - } - - res, err = pp.Decrypt(res) - if err != nil { - t.Fatal(err) - } - - fmt.Printf("[%s]\n", string(res)) - } - { - pp := new(Pcrypto) - pp.Init([]byte("Hana"), 1) - res, err := pp.Encrypt([]byte("Test Decrypt! 1")) - if err != nil { - t.Fatal(err) - } - - res, err = pp.Decrypt(res) - if err != nil { - t.Fatal(err) - } - - fmt.Printf("[%s]\n", string(res)) - } - { - pp := new(Pcrypto) - pp.Init([]byte("Hana"), 2) - res, err := pp.Encrypt([]byte("Test Decrypt! 2")) - if err != nil { - t.Fatal(err) - } - - res, err = pp.Decrypt(res) - if err != nil { - t.Fatal(err) - } - - fmt.Printf("[%s]\n", string(res)) - } - { - pp := new(Pcrypto) - pp.Init([]byte("Hana"), 3) - res, err := pp.Encrypt([]byte("Test Decrypt! 3")) - if err != nil { - t.Fatal(err) - } - - res, err = pp.Decrypt(res) - if err != nil { - t.Fatal(err) - } - - fmt.Printf("[%s]\n", string(res)) +func TestCompression(t *testing.T) { + testStr := "Test Compression!" + res, err := pp.Compression([]byte(testStr)) + if err != nil { + t.Fatalf("compression error: %v", err) } + res, err = pp.Decompression(res) + if err != nil { + t.Fatalf("decompression error: %v", err) + } + + if string(res) != testStr { + t.Fatalf("test compression error, from [%s] to [%s]", testStr, string(res)) + } } diff --git a/test/conf/auto_test_frpc.ini b/test/conf/auto_test_frpc.ini index eb3fdc6..e46d85b 100644 --- a/test/conf/auto_test_frpc.ini +++ b/test/conf/auto_test_frpc.ini @@ -11,8 +11,10 @@ type = tcp local_ip = 127.0.0.1 local_port = 10701 use_encryption = true +use_gzip = true [web] type = http local_ip = 127.0.0.1 local_port = 10702 +use_gzip = true diff --git a/test/run_test.sh b/test/run_test.sh index 82289a2..cc171f7 100755 --- a/test/run_test.sh +++ b/test/run_test.sh @@ -12,19 +12,19 @@ do sleep 1 str=`ss -ant|grep 10700|grep LISTEN` if [ -z "${str}" ]; then - echo "kong" + echo "wait" continue fi str=`ss -ant|grep 10710|grep LISTEN` if [ -z "${str}" ]; then - echo "kong" + echo "wait" continue fi str=`ss -ant|grep 10711|grep LISTEN` if [ -z "${str}" ]; then - echo "kong" + echo "wait" continue fi From b14441d5cd2b3de3b10d75912364af879e6d931f Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 24 Jun 2016 15:43:58 +0800 Subject: [PATCH 5/9] utils/vhost: update for vhost_https --- conf/frps.ini | 6 +- src/frp/cmd/frps/control.go | 7 +- src/frp/models/server/config.go | 20 +-- src/frp/utils/vhost/http.go | 47 +++++++ .../utils/vhost/{vhost_https.go => https.go} | 124 +++++++----------- src/frp/utils/vhost/vhost.go | 25 ---- 6 files changed, 108 insertions(+), 121 deletions(-) create mode 100644 src/frp/utils/vhost/http.go rename src/frp/utils/vhost/{vhost_https.go => https.go} (53%) diff --git a/conf/frps.ini b/conf/frps.ini index e96fb17..b4307e5 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -21,12 +21,14 @@ bind_addr = 0.0.0.0 listen_port = 6000 [web01] -type = https +# if type equals http, vhost_http_port must be set +type = http auth_token = 123 # if proxy type equals http, custom_domains must be set separated by commas custom_domains = web01.yourdomain.com,web01.yourdomain2.com [web02] -type = http +# if type equals https, vhost_https_port must be set +type = https auth_token = 123 custom_domains = web02.yourdomain.com diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index d66dd63..489b286 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -225,11 +225,16 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { } // check if vhost_port is set - if s.Type == "http" && server.VhostMuxer == nil { + if s.Type == "http" && server.VhostHttpMuxer == nil { info = fmt.Sprintf("ProxyName [%s], type [http] not support when vhost_http_port is not set", req.ProxyName) log.Warn(info) return } + if s.Type == "https" && server.VhostHttpsMuxer == nil { + info = fmt.Sprintf("ProxyName [%s], type [https] not support when vhost_https_port is not set", req.ProxyName) + log.Warn(info) + return + } // set infomations from frpc s.UseEncryption = req.UseEncryption diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index bc2345f..b426018 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -31,8 +31,8 @@ var ( ConfigFile string = "./frps.ini" BindAddr string = "0.0.0.0" BindPort int64 = 7000 - VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http - VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for http + VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http protocol + VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for https protocol DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available LogFile string = "console" LogWay string = "console" // console or file @@ -102,7 +102,6 @@ func loadCommonConf(confFile string) error { } else { VhostHttpsPort = 0 } - vhost.VhostHttpsPort = VhostHttpsPort tmpStr, ok = conf.Get("common", "dashboard_port") if ok { @@ -183,34 +182,25 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e // for http domainStr, ok := section["custom_domains"] if ok { - var suffix string - if VhostHttpPort != 80 { - suffix = fmt.Sprintf(":%d", VhostHttpPort) - } proxyServer.CustomDomains = strings.Split(domainStr, ",") if len(proxyServer.CustomDomains) == 0 { return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyServer.Name) } for i, domain := range proxyServer.CustomDomains { - proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + suffix + proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) } } } else if proxyServer.Type == "https" { // for https domainStr, ok := section["custom_domains"] if ok { - var suffix string - if VhostHttpsPort != 443 { - suffix = fmt.Sprintf(":%d", VhostHttpsPort) - } proxyServer.CustomDomains = strings.Split(domainStr, ",") if len(proxyServer.CustomDomains) == 0 { - return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyServer.Name) + return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals https", proxyServer.Name) } for i, domain := range proxyServer.CustomDomains { - proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + suffix + proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) } - log.Info("proxyServer: %+v", proxyServer.CustomDomains) } } proxyServers[proxyServer.Name] = proxyServer diff --git a/src/frp/utils/vhost/http.go b/src/frp/utils/vhost/http.go new file mode 100644 index 0000000..0f6aab5 --- /dev/null +++ b/src/frp/utils/vhost/http.go @@ -0,0 +1,47 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vhost + +import ( + "bufio" + "net" + "net/http" + "strings" + "time" + + "frp/utils/conn" +) + +type HttpMuxer struct { + *VhostMuxer +} + +func GetHttpHostname(c *conn.Conn) (_ net.Conn, routerName string, err error) { + sc, rd := newShareConn(c.TcpConn) + + request, err := http.ReadRequest(bufio.NewReader(rd)) + if err != nil { + return sc, "", err + } + tmpArr := strings.Split(request.Host, ":") + routerName = tmpArr[0] + request.Body.Close() + return sc, routerName, nil +} + +func NewHttpMuxer(listener *conn.Listener, timeout time.Duration) (*HttpMuxer, error) { + mux, err := NewVhostMuxer(listener, GetHttpHostname, timeout) + return &HttpMuxer{mux}, err +} diff --git a/src/frp/utils/vhost/vhost_https.go b/src/frp/utils/vhost/https.go similarity index 53% rename from src/frp/utils/vhost/vhost_https.go rename to src/frp/utils/vhost/https.go index 23d81ff..2fd61c6 100644 --- a/src/frp/utils/vhost/vhost_https.go +++ b/src/frp/utils/vhost/https.go @@ -15,25 +15,13 @@ package vhost import ( - _ "bufio" - _ "bytes" - _ "crypto/tls" - "errors" "fmt" - "frp/utils/conn" - "frp/utils/log" "io" - _ "io/ioutil" "net" - _ "net/http" "strings" - _ "sync" "time" -) -var ( - maxHandshake int64 = 65536 // maximum handshake we support (protocol max is 16 MB) - VhostHttpsPort int64 = 443 + "frp/utils/conn" ) const ( @@ -58,160 +46,140 @@ type HttpsMuxer struct { *VhostMuxer } -/* - RFC document: http://tools.ietf.org/html/rfc5246 -*/ - -func errMsgToLog(format string, a ...interface{}) error { - errMsg := fmt.Sprintf(format, a...) - log.Warn(errMsg) - return errors.New(errMsg) +func NewHttpsMuxer(listener *conn.Listener, timeout time.Duration) (*HttpsMuxer, error) { + mux, err := NewVhostMuxer(listener, GetHttpsHostname, timeout) + return &HttpsMuxer{mux}, err } -func readHandshake(rd io.Reader) (string, error) { - +func readHandshake(rd io.Reader) (host string, err error) { data := make([]byte, 1024) length, err := rd.Read(data) if err != nil { - return "", errMsgToLog("read err:%v", err) + return } else { if length < 47 { - return "", errMsgToLog("readHandshake: proto length[%d] is too short", length) + err = fmt.Errorf("readHandshake: proto length[%d] is too short", length) + return } } data = data[:length] - //log.Warn("data: %+v", data) if uint8(data[5]) != typeClientHello { - return "", errMsgToLog("readHandshake: type[%d] is not clientHello", uint16(data[5])) + err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5])) + return } - //version and random - //tlsVersion := uint16(data[9])<<8 | uint16(data[10]) - //random := data[11:43] - - //session + // session sessionIdLen := int(data[43]) if sessionIdLen > 32 || len(data) < 44+sessionIdLen { - return "", errMsgToLog("readHandshake: sessionIdLen[%d] is long", sessionIdLen) + err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIdLen) + return } data = data[44+sessionIdLen:] if len(data) < 2 { - return "", errMsgToLog("readHandshake: dataLen[%d] after session is short", len(data)) + err = fmt.Errorf("readHandshake: dataLen[%d] after session is short", len(data)) + return } // cipher suite numbers cipherSuiteLen := int(data[0])<<8 | int(data[1]) if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { - //return "", errMsgToLog("readHandshake: cipherSuiteLen[%d] is long", sessionIdLen) - return "", errMsgToLog("readHandshake: dataLen[%d] after cipher suite is short", len(data)) + err = fmt.Errorf("readHandshake: dataLen[%d] after cipher suite is short", len(data)) + return } data = data[2+cipherSuiteLen:] if len(data) < 1 { - return "", errMsgToLog("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen) + err = fmt.Errorf("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen) + return } - //compression method + // compression method compressionMethodsLen := int(data[0]) if len(data) < 1+compressionMethodsLen { - return "", errMsgToLog("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen) - //return false + err = fmt.Errorf("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen) + return } data = data[1+compressionMethodsLen:] - if len(data) == 0 { // ClientHello is optionally followed by extension data - //return true - return "", errMsgToLog("readHandshake: there is no extension data to get servername") + err = fmt.Errorf("readHandshake: there is no extension data to get servername") + return } if len(data) < 2 { - return "", errMsgToLog("readHandshake: extension dataLen[%d] is too short") + err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short") + return } extensionsLength := int(data[0])<<8 | int(data[1]) data = data[2:] if extensionsLength != len(data) { - return "", errMsgToLog("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data)) + err = fmt.Errorf("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data)) + return } for len(data) != 0 { if len(data) < 4 { - return "", errMsgToLog("readHandshake: extensionsDataLen[%d] is too short", len(data)) + err = fmt.Errorf("readHandshake: extensionsDataLen[%d] is too short", len(data)) + return } extension := uint16(data[0])<<8 | uint16(data[1]) length := int(data[2])<<8 | int(data[3]) data = data[4:] if len(data) < length { - return "", errMsgToLog("readHandshake: extensionLen[%d] is long", length) - //return false + err = fmt.Errorf("readHandshake: extensionLen[%d] is long", length) + return } switch extension { case extensionRenegotiationInfo: if length != 1 || data[0] != 0 { - return "", errMsgToLog("readHandshake: extension reNegotiationInfoLen[%d] is short", length) + err = fmt.Errorf("readHandshake: extension reNegotiationInfoLen[%d] is short", length) + return } case extensionNextProtoNeg: case extensionStatusRequest: case extensionServerName: d := data[:length] if len(d) < 2 { - return "", errMsgToLog("readHandshake: remiaining dataLen[%d] is short", len(d)) + err = fmt.Errorf("readHandshake: remiaining dataLen[%d] is short", len(d)) + return } namesLen := int(d[0])<<8 | int(d[1]) d = d[2:] if len(d) != namesLen { - return "", errMsgToLog("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d)) + err = fmt.Errorf("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d)) + return } for len(d) > 0 { if len(d) < 3 { - return "", errMsgToLog("readHandshake: extension serverNameLen[%d] is short", len(d)) + err = fmt.Errorf("readHandshake: extension serverNameLen[%d] is short", len(d)) + return } nameType := d[0] nameLen := int(d[1])<<8 | int(d[2]) d = d[3:] if len(d) < nameLen { - return "", errMsgToLog("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d)) + err = fmt.Errorf("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d)) + return } if nameType == 0 { - suffix := "" - if VhostHttpsPort != 443 { - suffix = fmt.Sprintf(":%d", VhostHttpsPort) - } serverName := string(d[:nameLen]) - domain := strings.ToLower(strings.TrimSpace(serverName)) + suffix - return domain, nil - break + host = strings.TrimSpace(serverName) + return host, nil } d = d[nameLen:] } } data = data[length:] } - //return "test.codermao.com:8082", nil - return "", errMsgToLog("Unknow error") + err = fmt.Errorf("Unknow error") + return } func GetHttpsHostname(c *conn.Conn) (sc net.Conn, routerName string, err error) { - log.Info("GetHttpsHostname") sc, rd := newShareConn(c.TcpConn) - host, err := readHandshake(rd) if err != nil { return sc, "", err } - /* - if _, ok := c.TcpConn.(*tls.Conn); ok { - log.Warn("convert to tlsConn success") - } else { - log.Warn("convert to tlsConn error") - }*/ - //tcpConn. - log.Debug("GetHttpsHostname: %s", host) - return sc, host, nil } - -func NewHttpsMuxer(listener *conn.Listener, timeout time.Duration) (*HttpsMuxer, error) { - mux, err := NewVhostMuxer(listener, GetHttpsHostname, timeout) - return &HttpsMuxer{mux}, err -} diff --git a/src/frp/utils/vhost/vhost.go b/src/frp/utils/vhost/vhost.go index d832f53..ecf080d 100644 --- a/src/frp/utils/vhost/vhost.go +++ b/src/frp/utils/vhost/vhost.go @@ -15,12 +15,10 @@ package vhost import ( - "bufio" "bytes" "fmt" "io" "net" - "net/http" "strings" "sync" "time" @@ -99,7 +97,6 @@ func (v *VhostMuxer) handle(c *conn.Conn) { } name = strings.ToLower(name) - l, ok := v.getListener(name) if !ok { return @@ -113,28 +110,6 @@ func (v *VhostMuxer) handle(c *conn.Conn) { l.accept <- c } -type HttpMuxer struct { - *VhostMuxer -} - -func GetHttpHostname(c *conn.Conn) (_ net.Conn, routerName string, err error) { - sc, rd := newShareConn(c.TcpConn) - - request, err := http.ReadRequest(bufio.NewReader(rd)) - if err != nil { - return sc, "", err - } - routerName = request.Host - request.Body.Close() - - return sc, routerName, nil -} - -func NewHttpMuxer(listener *conn.Listener, timeout time.Duration) (*HttpMuxer, error) { - mux, err := NewVhostMuxer(listener, GetHttpHostname, timeout) - return &HttpMuxer{mux}, err -} - type Listener struct { name string mux *VhostMuxer // for closing VhostMuxer From f968f3eaceea5b84ff30e5fc78d96bacd594dc28 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 26 Jun 2016 22:36:07 +0800 Subject: [PATCH 6/9] all: add new feature privilege mode --- conf/frpc.ini | 20 +++++++ conf/frps.ini | 3 ++ src/frp/cmd/frpc/control.go | 7 +++ src/frp/cmd/frps/control.go | 73 +++++++++++++++++++------ src/frp/cmd/frps/main.go | 3 ++ src/frp/models/client/client.go | 12 +++-- src/frp/models/client/config.go | 93 +++++++++++++++++++++++++------- src/frp/models/config/config.go | 1 + src/frp/models/msg/msg.go | 16 ++++-- src/frp/models/msg/process.go | 1 - src/frp/models/server/config.go | 52 ++++++++++++++++-- src/frp/models/server/server.go | 22 ++++++-- src/frp/utils/version/version.go | 2 +- 13 files changed, 252 insertions(+), 53 deletions(-) diff --git a/conf/frpc.ini b/conf/frpc.ini index 38101d1..3e4d36f 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -9,6 +9,8 @@ log_level = info log_max_days = 3 # for authentication auth_token = 123 +# for privilege mode +privilege_key = 12345678 # ssh is the proxy name same as server's configuration [ssh] @@ -32,3 +34,21 @@ use_gzip = true type = http local_ip = 127.0.0.1 local_port = 8000 + +[privilege_ssh] +# if privilege_mode is enabled, this proxy will be created automatically +privilege_mode = true +type = tcp +local_ip = 127.0.0.1 +local_port = 22 +use_encryption = true +use_gzip = false +remote_port = 6001 + +[privilege_web] +privilege_mode = true +type = http +local_ip = 127.0.0.1 +local_port = 80 +use_gzip = true +custom_domains = web03.yourdomain.com diff --git a/conf/frps.ini b/conf/frps.ini index b4307e5..5c96939 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -12,6 +12,9 @@ log_file = ./frps.log # debug, info, warn, error log_level = info log_max_days = 3 +# if you enable privilege mode, frpc can create a proxy without pre-configure in frps when privilege_key is correct +privilege_mode = true +privilege_key = 12345678 # ssh is the proxy name, client will use this name and auth_token to connect to server [ssh] diff --git a/src/frp/cmd/frpc/control.go b/src/frp/cmd/frpc/control.go index 57ec52f..fe22fc5 100644 --- a/src/frp/cmd/frpc/control.go +++ b/src/frp/cmd/frpc/control.go @@ -144,8 +144,15 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { AuthKey: authKey, UseEncryption: cli.UseEncryption, UseGzip: cli.UseGzip, + PrivilegeMode: cli.PrivilegeMode, + ProxyType: cli.Type, Timestamp: nowTime, } + if cli.PrivilegeMode { + req.RemotePort = cli.RemotePort + req.CustomDomains = cli.CustomDomains + } + buf, _ := json.Marshal(req) err = c.Write(string(buf) + "\n") if err != nil { diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index 489b286..53a976a 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -194,30 +194,68 @@ func msgSender(s *server.ProxyServer, c *conn.Conn, msgSendChan chan interface{} // if success, ret equals 0, otherwise greater than 0 func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { ret = 1 - // check if proxy name exist - s, ok := server.ProxyServers[req.ProxyName] - if !ok { - info = fmt.Sprintf("ProxyName [%s] is not exist", req.ProxyName) - log.Warn(info) + if req.PrivilegeMode && !server.PrivilegeMode { + info = fmt.Sprintf("ProxyName [%s], PrivilegeMode is disabled in frps", req.ProxyName) + log.Warn("info") return } - // check authKey + var ( + s *server.ProxyServer + ok bool + ) + s, ok = server.ProxyServers[req.ProxyName] + if req.PrivilegeMode && req.Type == consts.NewCtlConn { + log.Debug("ProxyName [%s], doLogin and privilege mode is enabled", req.ProxyName) + } else { + if !ok { + info = fmt.Sprintf("ProxyName [%s] is not exist", req.ProxyName) + log.Warn(info) + return + } + } + + // check authKey or privilegeKey nowTime := time.Now().Unix() - authKey := pcrypto.GetAuthKey(req.ProxyName + s.AuthToken + fmt.Sprintf("%d", req.Timestamp)) - // authKey avaiable in 15 minutes - if nowTime-req.Timestamp > 15*60 { - info = fmt.Sprintf("ProxyName [%s], authorization timeout", req.ProxyName) - log.Warn(info) - return - } else if req.AuthKey != authKey { - info = fmt.Sprintf("ProxyName [%s], authorization failed", req.ProxyName) - log.Warn(info) - return + if req.PrivilegeMode { + privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeKey + fmt.Sprintf("%d", req.Timestamp)) + // privilegeKey avaiable in 15 minutes + if nowTime-req.Timestamp > 15*60 { + info = fmt.Sprintf("ProxyName [%s], privilege mode authorization timeout", req.ProxyName) + log.Warn(info) + return + } else if req.AuthKey != privilegeKey { + log.Debug("%s %s", req.AuthKey, privilegeKey) + info = fmt.Sprintf("ProxyName [%s], privilege mode authorization failed", req.ProxyName) + log.Warn(info) + return + } + } else { + authKey := pcrypto.GetAuthKey(req.ProxyName + s.AuthToken + fmt.Sprintf("%d", req.Timestamp)) + // authKey avaiable in 15 minutes + if nowTime-req.Timestamp > 15*60 { + info = fmt.Sprintf("ProxyName [%s], authorization timeout", req.ProxyName) + log.Warn(info) + return + } else if req.AuthKey != authKey { + info = fmt.Sprintf("ProxyName [%s], authorization failed", req.ProxyName) + log.Warn(info) + return + } } // control conn if req.Type == consts.NewCtlConn { + if req.PrivilegeMode { + s = server.NewProxyServerFromCtlMsg(req) + err := server.CreateProxy(s) + if err != nil { + info = fmt.Sprintf("ProxyName [%s], %v", req.ProxyName, err) + log.Warn(info) + return + } + } + if s.Status == consts.Working { info = fmt.Sprintf("ProxyName [%s], already in use", req.ProxyName) log.Warn(info) @@ -248,6 +286,9 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { return } log.Info("ProxyName [%s], start proxy success", req.ProxyName) + if req.PrivilegeMode { + log.Info("ProxyName [%s], created by PrivilegeMode", req.ProxyName) + } } else if req.Type == consts.NewWorkConn { // work conn if s.Status != consts.Working { diff --git a/src/frp/cmd/frps/main.go b/src/frp/cmd/frps/main.go index de7f7be..a9d3f7e 100644 --- a/src/frp/cmd/frps/main.go +++ b/src/frp/cmd/frps/main.go @@ -172,5 +172,8 @@ func main() { } log.Info("Start frps success") + if server.PrivilegeMode == true { + log.Info("PrivilegeMode is enabled, you should pay more attention to security issues") + } ProcessControlConn(l) } diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index 95c5931..d59c5ec 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -31,6 +31,9 @@ type ProxyClient struct { config.BaseConf LocalIp string LocalPort int64 + + RemotePort int64 + CustomDomains []string } func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { @@ -57,10 +60,11 @@ func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err nowTime := time.Now().Unix() authKey := pcrypto.GetAuthKey(p.Name + p.AuthToken + fmt.Sprintf("%d", nowTime)) req := &msg.ControlReq{ - Type: consts.NewWorkConn, - ProxyName: p.Name, - AuthKey: authKey, - Timestamp: nowTime, + Type: consts.NewWorkConn, + ProxyName: p.Name, + AuthKey: authKey, + PrivilegeMode: p.PrivilegeMode, + Timestamp: nowTime, } buf, _ := json.Marshal(req) diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 132daf6..accdf1e 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -17,6 +17,7 @@ package client import ( "fmt" "strconv" + "strings" ini "github.com/vaughan0/go-ini" ) @@ -29,6 +30,7 @@ var ( LogWay string = "console" LogLevel string = "info" LogMaxDays int64 = 3 + PrivilegeKey string = "" HeartBeatInterval int64 = 20 HeartBeatTimeout int64 = 90 ) @@ -75,12 +77,15 @@ func LoadConf(confFile string) (err error) { LogMaxDays, _ = strconv.ParseInt(tmpStr, 10, 64) } + tmpStr, ok = conf.Get("common", "privilege_key") + if ok { + PrivilegeKey = tmpStr + } + var authToken string tmpStr, ok = conf.Get("common", "auth_token") if ok { authToken = tmpStr - } else { - return fmt.Errorf("auth_token not found") } // proxies @@ -90,9 +95,6 @@ func LoadConf(confFile string) (err error) { // name proxyClient.Name = name - // auth_token - proxyClient.AuthToken = authToken - // local_ip proxyClient.LocalIp, ok = section["local_ip"] if !ok { @@ -101,46 +103,101 @@ func LoadConf(confFile string) (err error) { } // local_port - portStr, ok := section["local_port"] + tmpStr, ok = section["local_port"] if ok { - proxyClient.LocalPort, err = strconv.ParseInt(portStr, 10, 64) + proxyClient.LocalPort, err = strconv.ParseInt(tmpStr, 10, 64) if err != nil { - return fmt.Errorf("Parse ini file error: proxy [%s] local_port error", proxyClient.Name) + return fmt.Errorf("Parse conf error: proxy [%s] local_port error", proxyClient.Name) } } else { - return fmt.Errorf("Parse ini file error: proxy [%s] local_port not found", proxyClient.Name) + return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", proxyClient.Name) } // type proxyClient.Type = "tcp" - typeStr, ok := section["type"] + tmpStr, ok = section["type"] if ok { - if typeStr != "tcp" && typeStr != "http" && typeStr != "https" { - return fmt.Errorf("Parse ini file error: proxy [%s] type error", proxyClient.Name) + if tmpStr != "tcp" && tmpStr != "http" && tmpStr != "https" { + return fmt.Errorf("Parse conf error: proxy [%s] type error", proxyClient.Name) } - proxyClient.Type = typeStr + proxyClient.Type = tmpStr } // use_encryption proxyClient.UseEncryption = false - useEncryptionStr, ok := section["use_encryption"] - if ok && useEncryptionStr == "true" { + tmpStr, ok = section["use_encryption"] + if ok && tmpStr == "true" { proxyClient.UseEncryption = true } // use_gzip proxyClient.UseGzip = false - useGzipStr, ok := section["use_gzip"] - if ok && useGzipStr == "true" { + tmpStr, ok = section["use_gzip"] + if ok && tmpStr == "true" { proxyClient.UseGzip = true } + // privilege_mode + proxyClient.PrivilegeMode = false + tmpStr, ok = section["privilege_mode"] + if ok && tmpStr == "true" { + proxyClient.PrivilegeMode = true + } + + // configures used in privilege mode + if proxyClient.PrivilegeMode == true { + // auth_token + proxyClient.AuthToken = PrivilegeKey + + if proxyClient.Type == "tcp" { + // remote_port + tmpStr, ok = section["remote_port"] + if ok { + proxyClient.RemotePort, err = strconv.ParseInt(tmpStr, 10, 64) + if err != nil { + return fmt.Errorf("Parse conf error: proxy [%s] remote_port error", proxyClient.Name) + } + } else { + return fmt.Errorf("Parse conf error: proxy [%s] remote_port not found", proxyClient.Name) + } + } else if proxyClient.Type == "http" { + domainStr, ok := section["custom_domains"] + if ok { + proxyClient.CustomDomains = strings.Split(domainStr, ",") + if len(proxyClient.CustomDomains) == 0 { + return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name) + } + for i, domain := range proxyClient.CustomDomains { + proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + } + } else { + return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name) + } + } else if proxyClient.Type == "https" { + domainStr, ok := section["custom_domains"] + if ok { + proxyClient.CustomDomains = strings.Split(domainStr, ",") + if len(proxyClient.CustomDomains) == 0 { + return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals https", proxyClient.Name) + } + for i, domain := range proxyClient.CustomDomains { + proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + } + } else { + return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name) + } + } + } else /* proxyClient.PrivilegeMode == false */ { + // authToken + proxyClient.AuthToken = authToken + } + ProxyClients[proxyClient.Name] = proxyClient } } if len(ProxyClients) == 0 { - return fmt.Errorf("Parse ini file error: no proxy config found") + return fmt.Errorf("Parse conf error: no proxy config found") } return nil diff --git a/src/frp/models/config/config.go b/src/frp/models/config/config.go index 3ef4889..1e281ca 100644 --- a/src/frp/models/config/config.go +++ b/src/frp/models/config/config.go @@ -20,4 +20,5 @@ type BaseConf struct { Type string UseEncryption bool UseGzip bool + PrivilegeMode bool } diff --git a/src/frp/models/msg/msg.go b/src/frp/models/msg/msg.go index e3ea2b2..d659007 100644 --- a/src/frp/models/msg/msg.go +++ b/src/frp/models/msg/msg.go @@ -22,11 +22,17 @@ type GeneralRes struct { // messages between control connections of frpc and frps type ControlReq struct { Type int64 `json:"type"` - ProxyName string `json:"proxy_name,omitempty"` - AuthKey string `json:"auth_key, omitempty"` - UseEncryption bool `json:"use_encryption, omitempty"` - UseGzip bool `json:"use_gzip, omitempty"` - Timestamp int64 `json:"timestamp, omitempty"` + ProxyName string `json:"proxy_name"` + AuthKey string `json:"auth_key"` + UseEncryption bool `json:"use_encryption"` + UseGzip bool `json:"use_gzip"` + + // configures used if privilege_mode is enabled + PrivilegeMode bool `json:"privilege_mode"` + ProxyType string `json:"proxy_type"` + RemotePort int64 `json:"remote_port"` + CustomDomains []string `json:"custom_domains, omitempty"` + Timestamp int64 `json:"timestamp"` } type ControlRes struct { diff --git a/src/frp/models/msg/process.go b/src/frp/models/msg/process.go index b845269..19a1d40 100644 --- a/src/frp/models/msg/process.go +++ b/src/frp/models/msg/process.go @@ -141,7 +141,6 @@ func pipeDecrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { } // gzip if conf.UseGzip { - log.Warn("%x", res) res, err = laes.Decompression(res) if err != nil { log.Warn("ProxyName [%s], decompression error, %v", conf.Name, err) diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index b426018..d586cd0 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -22,6 +22,7 @@ import ( ini "github.com/vaughan0/go-ini" + "frp/models/consts" "frp/utils/log" "frp/utils/vhost" ) @@ -38,6 +39,8 @@ var ( LogWay string = "console" // console or file LogLevel string = "info" LogMaxDays int64 = 3 + PrivilegeMode bool = false + PrivilegeKey string = "" HeartBeatTimeout int64 = 90 UserConnTimeout int64 = 10 @@ -132,6 +135,22 @@ func loadCommonConf(confFile string) error { LogMaxDays = v } } + + tmpStr, ok = conf.Get("common", "privilege_mode") + if ok { + if tmpStr == "true" { + PrivilegeMode = true + } + } + + if PrivilegeMode == true { + tmpStr, ok = conf.Get("common", "privilege_key") + if ok { + PrivilegeKey = tmpStr + } else { + return fmt.Errorf("Parse conf error: privilege_key must be set if privilege_mode is enabled") + } + } return nil } @@ -189,6 +208,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e for i, domain := range proxyServer.CustomDomains { proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) } + } else { + return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyServer.Name) } } else if proxyServer.Type == "https" { // for https @@ -201,6 +222,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e for i, domain := range proxyServer.CustomDomains { proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) } + } else { + return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals https", proxyServer.Name) } } proxyServers[proxyServer.Name] = proxyServer @@ -234,14 +257,37 @@ func ReloadConf(confFile string) (err error) { } } + // proxies created by PrivilegeMode won't be deleted for name, oldProxyServer := range ProxyServers { _, ok := loadProxyServers[name] if !ok { - oldProxyServer.Close() - delete(ProxyServers, name) - log.Info("ProxyName [%s] deleted, close it", name) + if !oldProxyServer.PrivilegeMode { + oldProxyServer.Close() + delete(ProxyServers, name) + log.Info("ProxyName [%s] deleted, close it", name) + } else { + log.Info("ProxyName [%s] created by PrivilegeMode, won't be closed", name) + } } } ProxyServersMutex.Unlock() return nil } + +func CreateProxy(s *ProxyServer) error { + ProxyServersMutex.Lock() + defer ProxyServersMutex.Unlock() + oldServer, ok := ProxyServers[s.Name] + if ok { + if oldServer.Status == consts.Working { + return fmt.Errorf("this proxy is already working now") + } + oldServer.Close() + if oldServer.PrivilegeMode { + delete(ProxyServers, s.Name) + } + } + s.Init() + ProxyServers[s.Name] = s + return nil +} diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index 65154af..9e14e03 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -52,6 +52,20 @@ func NewProxyServer() (p *ProxyServer) { return p } +func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) { + p = &ProxyServer{} + p.Name = req.ProxyName + p.Type = req.ProxyType + p.UseEncryption = req.UseEncryption + p.UseGzip = req.UseGzip + p.PrivilegeMode = req.PrivilegeMode + p.BindAddr = BindAddr + p.ListenPort = req.RemotePort + p.CustomDomains = req.CustomDomains + p.AuthToken = PrivilegeKey + return +} + func (p *ProxyServer) Init() { p.Lock() p.Status = consts.Idle @@ -161,11 +175,9 @@ func (p *ProxyServer) Close() { p.Lock() if p.Status != consts.Closed { p.Status = consts.Closed - if len(p.listeners) != 0 { - for _, l := range p.listeners { - if l != nil { - l.Close() - } + for _, l := range p.listeners { + if l != nil { + l.Close() } } close(p.ctlMsgChan) diff --git a/src/frp/utils/version/version.go b/src/frp/utils/version/version.go index 842028d..9559803 100644 --- a/src/frp/utils/version/version.go +++ b/src/frp/utils/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.6.0" +var version string = "0.7.0" func Full() string { return version From ba74934a1f3c520112aa630e13eab24e8ff2c846 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 28 Jun 2016 00:21:13 +0800 Subject: [PATCH 7/9] all: privilege mode update --- conf/frpc.ini | 2 +- conf/frps.ini | 4 ++-- src/frp/cmd/frpc/control.go | 7 +++++-- src/frp/cmd/frps/control.go | 5 ++--- src/frp/models/client/client.go | 9 +++++++-- src/frp/models/client/config.go | 19 +++++++++++-------- src/frp/models/config/config.go | 13 +++++++------ src/frp/models/msg/msg.go | 1 + src/frp/models/msg/process.go | 12 ++++++++++-- src/frp/models/server/config.go | 11 +++++++---- src/frp/models/server/server.go | 2 +- 11 files changed, 54 insertions(+), 31 deletions(-) diff --git a/conf/frpc.ini b/conf/frpc.ini index 3e4d36f..bbab6d0 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -10,7 +10,7 @@ log_max_days = 3 # for authentication auth_token = 123 # for privilege mode -privilege_key = 12345678 +privilege_token = 12345678 # ssh is the proxy name same as server's configuration [ssh] diff --git a/conf/frps.ini b/conf/frps.ini index 5c96939..aada47b 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -12,9 +12,9 @@ log_file = ./frps.log # debug, info, warn, error log_level = info log_max_days = 3 -# if you enable privilege mode, frpc can create a proxy without pre-configure in frps when privilege_key is correct +# if you enable privilege mode, frpc can create a proxy without pre-configure in frps when privilege_token is correct privilege_mode = true -privilege_key = 12345678 +privilege_token = 12345678 # ssh is the proxy name, client will use this name and auth_token to connect to server [ssh] diff --git a/src/frp/cmd/frpc/control.go b/src/frp/cmd/frpc/control.go index fe22fc5..9e8c3e6 100644 --- a/src/frp/cmd/frpc/control.go +++ b/src/frp/cmd/frpc/control.go @@ -137,11 +137,9 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { } nowTime := time.Now().Unix() - authKey := pcrypto.GetAuthKey(cli.Name + cli.AuthToken + fmt.Sprintf("%d", nowTime)) req := &msg.ControlReq{ Type: consts.NewCtlConn, ProxyName: cli.Name, - AuthKey: authKey, UseEncryption: cli.UseEncryption, UseGzip: cli.UseGzip, PrivilegeMode: cli.PrivilegeMode, @@ -149,8 +147,13 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { Timestamp: nowTime, } if cli.PrivilegeMode { + privilegeKey := pcrypto.GetAuthKey(cli.Name + client.PrivilegeToken + fmt.Sprintf("%d", nowTime)) req.RemotePort = cli.RemotePort req.CustomDomains = cli.CustomDomains + req.PrivilegeKey = privilegeKey + } else { + authKey := pcrypto.GetAuthKey(cli.Name + cli.AuthToken + fmt.Sprintf("%d", nowTime)) + req.AuthKey = authKey } buf, _ := json.Marshal(req) diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index 53a976a..0e46088 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -218,14 +218,13 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) { // check authKey or privilegeKey nowTime := time.Now().Unix() if req.PrivilegeMode { - privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeKey + fmt.Sprintf("%d", req.Timestamp)) + privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeToken + fmt.Sprintf("%d", req.Timestamp)) // privilegeKey avaiable in 15 minutes if nowTime-req.Timestamp > 15*60 { info = fmt.Sprintf("ProxyName [%s], privilege mode authorization timeout", req.ProxyName) log.Warn(info) return - } else if req.AuthKey != privilegeKey { - log.Debug("%s %s", req.AuthKey, privilegeKey) + } else if req.PrivilegeKey != privilegeKey { info = fmt.Sprintf("ProxyName [%s], privilege mode authorization failed", req.ProxyName) log.Warn(info) return diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index d59c5ec..bdd4279 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -58,14 +58,19 @@ func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err } nowTime := time.Now().Unix() - authKey := pcrypto.GetAuthKey(p.Name + p.AuthToken + fmt.Sprintf("%d", nowTime)) req := &msg.ControlReq{ Type: consts.NewWorkConn, ProxyName: p.Name, - AuthKey: authKey, PrivilegeMode: p.PrivilegeMode, Timestamp: nowTime, } + if p.PrivilegeMode == true { + privilegeKey := pcrypto.GetAuthKey(p.Name + PrivilegeToken + fmt.Sprintf("%d", nowTime)) + req.PrivilegeKey = privilegeKey + } else { + authKey := pcrypto.GetAuthKey(p.Name + p.AuthToken + fmt.Sprintf("%d", nowTime)) + req.AuthKey = authKey + } buf, _ := json.Marshal(req) err = c.Write(string(buf) + "\n") diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index accdf1e..0d942e9 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -30,7 +30,7 @@ var ( LogWay string = "console" LogLevel string = "info" LogMaxDays int64 = 3 - PrivilegeKey string = "" + PrivilegeToken string = "" HeartBeatInterval int64 = 20 HeartBeatTimeout int64 = 90 ) @@ -77,9 +77,9 @@ func LoadConf(confFile string) (err error) { LogMaxDays, _ = strconv.ParseInt(tmpStr, 10, 64) } - tmpStr, ok = conf.Get("common", "privilege_key") + tmpStr, ok = conf.Get("common", "privilege_token") if ok { - PrivilegeKey = tmpStr + PrivilegeToken = tmpStr } var authToken string @@ -95,6 +95,9 @@ func LoadConf(confFile string) (err error) { // name proxyClient.Name = name + // auth_token + proxyClient.AuthToken = authToken + // local_ip proxyClient.LocalIp, ok = section["local_ip"] if !ok { @@ -146,8 +149,11 @@ func LoadConf(confFile string) (err error) { // configures used in privilege mode if proxyClient.PrivilegeMode == true { - // auth_token - proxyClient.AuthToken = PrivilegeKey + if PrivilegeToken == "" { + return fmt.Errorf("Parse conf error: proxy [%s] privilege_key must be set when privilege_mode = true", proxyClient.Name) + } else { + proxyClient.PrivilegeToken = PrivilegeToken + } if proxyClient.Type == "tcp" { // remote_port @@ -187,9 +193,6 @@ func LoadConf(confFile string) (err error) { return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name) } } - } else /* proxyClient.PrivilegeMode == false */ { - // authToken - proxyClient.AuthToken = authToken } ProxyClients[proxyClient.Name] = proxyClient diff --git a/src/frp/models/config/config.go b/src/frp/models/config/config.go index 1e281ca..14200eb 100644 --- a/src/frp/models/config/config.go +++ b/src/frp/models/config/config.go @@ -15,10 +15,11 @@ package config type BaseConf struct { - Name string - AuthToken string - Type string - UseEncryption bool - UseGzip bool - PrivilegeMode bool + Name string + AuthToken string + Type string + UseEncryption bool + UseGzip bool + PrivilegeMode bool + PrivilegeToken string } diff --git a/src/frp/models/msg/msg.go b/src/frp/models/msg/msg.go index d659007..e89bce1 100644 --- a/src/frp/models/msg/msg.go +++ b/src/frp/models/msg/msg.go @@ -29,6 +29,7 @@ type ControlReq struct { // configures used if privilege_mode is enabled PrivilegeMode bool `json:"privilege_mode"` + PrivilegeKey string `json:"privilege_key"` ProxyType string `json:"proxy_type"` RemotePort int64 `json:"remote_port"` CustomDomains []string `json:"custom_domains, omitempty"` diff --git a/src/frp/models/msg/process.go b/src/frp/models/msg/process.go index 19a1d40..cfc782b 100644 --- a/src/frp/models/msg/process.go +++ b/src/frp/models/msg/process.go @@ -104,7 +104,11 @@ func unpkgMsg(data []byte) (int, []byte, []byte) { // decrypt msg from reader, then write into writer func pipeDecrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(conf.AuthToken)); err != nil { + key := conf.AuthToken + if conf.PrivilegeMode { + key = conf.PrivilegeToken + } + if err := laes.Init([]byte(key)); err != nil { log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) return fmt.Errorf("Pcrypto Init error: %v", err) } @@ -159,7 +163,11 @@ func pipeDecrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { // recvive msg from reader, then encrypt msg into writer func pipeEncrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) { laes := new(pcrypto.Pcrypto) - if err := laes.Init([]byte(conf.AuthToken)); err != nil { + key := conf.AuthToken + if conf.PrivilegeMode { + key = conf.PrivilegeToken + } + if err := laes.Init([]byte(key)); err != nil { log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) return fmt.Errorf("Pcrypto Init error: %v", err) } diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index d586cd0..8657ec6 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -40,7 +40,7 @@ var ( LogLevel string = "info" LogMaxDays int64 = 3 PrivilegeMode bool = false - PrivilegeKey string = "" + PrivilegeToken string = "" HeartBeatTimeout int64 = 90 UserConnTimeout int64 = 10 @@ -144,11 +144,14 @@ func loadCommonConf(confFile string) error { } if PrivilegeMode == true { - tmpStr, ok = conf.Get("common", "privilege_key") + tmpStr, ok = conf.Get("common", "privilege_token") if ok { - PrivilegeKey = tmpStr + if tmpStr == "" { + return fmt.Errorf("Parse conf error: privilege_token can not be null") + } + PrivilegeToken = tmpStr } else { - return fmt.Errorf("Parse conf error: privilege_key must be set if privilege_mode is enabled") + return fmt.Errorf("Parse conf error: privilege_token must be set if privilege_mode is enabled") } } return nil diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index 9e14e03..ad85514 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -59,10 +59,10 @@ func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) { p.UseEncryption = req.UseEncryption p.UseGzip = req.UseGzip p.PrivilegeMode = req.PrivilegeMode + p.PrivilegeToken = PrivilegeToken p.BindAddr = BindAddr p.ListenPort = req.RemotePort p.CustomDomains = req.CustomDomains - p.AuthToken = PrivilegeKey return } From e580c7b6e62b1d756f55e157470cf47596b7a36d Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 28 Jun 2016 00:55:30 +0800 Subject: [PATCH 8/9] all: release resources when proxy closed using privilege mode --- src/frp/models/server/config.go | 6 ++++++ src/frp/models/server/server.go | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 8657ec6..1d49c66 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -294,3 +294,9 @@ func CreateProxy(s *ProxyServer) error { ProxyServers[s.Name] = s return nil } + +func DeleteProxy(proxyName string) { + ProxyServersMutex.Lock() + defer ProxyServersMutex.Unlock() + delete(ProxyServers, proxyName) +} diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index ad85514..b15f5cf 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -186,6 +186,10 @@ func (p *ProxyServer) Close() { p.CtlConn.Close() } } + // if the proxy created by PrivilegeMode, delete it when closed + if p.PrivilegeMode { + DeleteProxy(p.Name) + } p.Unlock() } From e99357da4e2ac8ffc76cbc497a83c0413c04f63b Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 4 Jul 2016 15:08:16 +0800 Subject: [PATCH 9/9] doc: add contributor --- README.md | 1 + README_zh.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index d0900e6..57ae6cc 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ Interested in getting involved? We would like to help you! * [fatedier](https://github.com/fatedier) * [Hurricanezwf](https://github.com/Hurricanezwf) * [vashstorm](https://github.com/vashstorm) +* [maodanp](https://github.com/maodanp) diff --git a/README_zh.md b/README_zh.md index 7138556..91c14b8 100644 --- a/README_zh.md +++ b/README_zh.md @@ -45,3 +45,4 @@ frp 目前正在前期开发阶段,master 分支用于发布稳定版本,dev * [fatedier](https://github.com/fatedier) * [Hurricanezwf](https://github.com/Hurricanezwf) * [vashstorm](https://github.com/vashstorm) +* [maodanp](https://github.com/maodanp)