all: modify import path, change version to v0.8.0

This commit is contained in:
fatedier
2016-08-11 16:10:44 +08:00
parent 9c0ca8675d
commit d4ecc2218d
32 changed files with 59 additions and 59 deletions

View File

@@ -0,0 +1,87 @@
// 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 broadcast
type Broadcast struct {
listeners []chan interface{}
reg chan (chan interface{})
unreg chan (chan interface{})
in chan interface{}
stop chan int64
stopStatus bool
}
func NewBroadcast() *Broadcast {
b := &Broadcast{
listeners: make([]chan interface{}, 0),
reg: make(chan (chan interface{})),
unreg: make(chan (chan interface{})),
in: make(chan interface{}),
stop: make(chan int64),
stopStatus: false,
}
go func() {
for {
select {
case l := <-b.unreg:
// remove L from b.listeners
// this operation is slow: O(n) but not used frequently
// unlike iterating over listeners
oldListeners := b.listeners
b.listeners = make([]chan interface{}, 0, len(oldListeners))
for _, oldL := range oldListeners {
if l != oldL {
b.listeners = append(b.listeners, oldL)
}
}
case l := <-b.reg:
b.listeners = append(b.listeners, l)
case item := <-b.in:
for _, l := range b.listeners {
l <- item
}
case _ = <-b.stop:
b.stopStatus = true
break
}
}
}()
return b
}
func (b *Broadcast) In() chan interface{} {
return b.in
}
func (b *Broadcast) Reg() chan interface{} {
listener := make(chan interface{})
b.reg <- listener
return listener
}
func (b *Broadcast) UnReg(listener chan interface{}) {
b.unreg <- listener
}
func (b *Broadcast) Close() {
if b.stopStatus == false {
b.stop <- 1
}
}

View File

@@ -0,0 +1,77 @@
// 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 broadcast
import (
"sync"
"testing"
"time"
)
var (
totalNum int = 5
succNum int = 0
mutex sync.Mutex
)
func TestBroadcast(t *testing.T) {
b := NewBroadcast()
if b == nil {
t.Fatalf("New Broadcast error, nil return")
}
defer b.Close()
var wait sync.WaitGroup
wait.Add(totalNum)
for i := 0; i < totalNum; i++ {
go worker(b, &wait)
}
time.Sleep(1e6 * 20)
msg := "test"
b.In() <- msg
wait.Wait()
if succNum != totalNum {
t.Fatalf("TotalNum %d, FailNum(timeout) %d", totalNum, totalNum-succNum)
}
}
func worker(b *Broadcast, wait *sync.WaitGroup) {
defer wait.Done()
msgChan := b.Reg()
// exit if nothing got in 2 seconds
timeout := make(chan bool, 1)
go func() {
time.Sleep(time.Duration(2) * time.Second)
timeout <- true
}()
select {
case item := <-msgChan:
msg := item.(string)
if msg == "test" {
mutex.Lock()
succNum++
mutex.Unlock()
} else {
break
}
case <-timeout:
break
}
}

218
src/utils/conn/conn.go Normal file
View File

@@ -0,0 +1,218 @@
// 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 conn
import (
"bufio"
"fmt"
"io"
"net"
"strings"
"sync"
"time"
)
type Listener struct {
addr net.Addr
l *net.TCPListener
accept chan *Conn
closeFlag bool
}
func Listen(bindAddr string, bindPort int64) (l *Listener, err error) {
tcpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", bindAddr, bindPort))
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
return l, err
}
l = &Listener{
addr: listener.Addr(),
l: listener,
accept: make(chan *Conn),
closeFlag: false,
}
go func() {
for {
conn, err := l.l.AcceptTCP()
if err != nil {
if l.closeFlag {
return
}
continue
}
c := &Conn{
TcpConn: conn,
closeFlag: false,
}
c.Reader = bufio.NewReader(c.TcpConn)
l.accept <- c
}
}()
return l, err
}
// wait util get one new connection or listener is closed
// if listener is closed, err returned
func (l *Listener) Accept() (*Conn, error) {
conn, ok := <-l.accept
if !ok {
return conn, fmt.Errorf("channel close")
}
return conn, nil
}
func (l *Listener) Close() error {
if l.l != nil && l.closeFlag == false {
l.closeFlag = true
l.l.Close()
close(l.accept)
}
return nil
}
// wrap for TCPConn
type Conn struct {
TcpConn net.Conn
Reader *bufio.Reader
closeFlag bool
mutex sync.RWMutex
}
func NewConn(conn net.Conn) (c *Conn) {
c = &Conn{}
c.TcpConn = conn
c.Reader = bufio.NewReader(c.TcpConn)
c.closeFlag = false
return c
}
func ConnectServer(host string, port int64) (c *Conn, err error) {
c = &Conn{}
servertAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return
}
conn, err := net.DialTCP("tcp", nil, servertAddr)
if err != nil {
return
}
c.TcpConn = conn
c.Reader = bufio.NewReader(c.TcpConn)
c.closeFlag = false
return c, nil
}
// if the tcpConn is different with c.TcpConn
// you should call c.Close() first
func (c *Conn) SetTcpConn(tcpConn net.Conn) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.TcpConn = tcpConn
c.closeFlag = false
c.Reader = bufio.NewReader(c.TcpConn)
}
func (c *Conn) GetRemoteAddr() (addr string) {
return c.TcpConn.RemoteAddr().String()
}
func (c *Conn) GetLocalAddr() (addr string) {
return c.TcpConn.LocalAddr().String()
}
func (c *Conn) Read(p []byte) (n int, err error) {
n, err = c.Reader.Read(p)
return
}
func (c *Conn) ReadLine() (buff string, err error) {
buff, err = c.Reader.ReadString('\n')
if err != nil {
// wsarecv error in windows means connection closed?
if err == io.EOF || strings.Contains(err.Error(), "wsarecv") {
c.mutex.Lock()
c.closeFlag = true
c.mutex.Unlock()
}
}
return buff, err
}
func (c *Conn) WriteBytes(content []byte) (n int, err error) {
n, err = c.TcpConn.Write(content)
return
}
func (c *Conn) Write(content string) (err error) {
_, err = c.TcpConn.Write([]byte(content))
return err
}
func (c *Conn) SetDeadline(t time.Time) error {
return c.TcpConn.SetDeadline(t)
}
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.TcpConn.SetReadDeadline(t)
}
func (c *Conn) Close() {
c.mutex.Lock()
if c.TcpConn != nil && c.closeFlag == false {
c.closeFlag = true
c.TcpConn.Close()
}
c.mutex.Unlock()
}
func (c *Conn) IsClosed() (closeFlag bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
closeFlag = c.closeFlag
return
}
// when you call this function, you should make sure that
// remote client won't send any bytes to this socket
func (c *Conn) CheckClosed() bool {
c.mutex.RLock()
if c.closeFlag {
return true
}
c.mutex.RUnlock()
// err := c.TcpConn.SetReadDeadline(time.Now().Add(100 * time.Microsecond))
err := c.TcpConn.SetReadDeadline(time.Now().Add(time.Millisecond))
if err != nil {
c.Close()
return true
}
var tmp []byte = make([]byte, 1)
_, err = c.TcpConn.Read(tmp)
if err == io.EOF {
return true
}
err = c.TcpConn.SetReadDeadline(time.Time{})
if err != nil {
c.Close()
return true
}
return false
}

78
src/utils/log/log.go Normal file
View File

@@ -0,0 +1,78 @@
// 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 log
import (
"fmt"
"github.com/astaxie/beego/logs"
)
var Log *logs.BeeLogger
func init() {
Log = logs.NewLogger(1000)
Log.EnableFuncCallDepth(true)
Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
}
func InitLog(logWay string, logFile string, logLevel string, maxdays int64) {
SetLogFile(logWay, logFile, maxdays)
SetLogLevel(logLevel)
}
// logWay: file or console
func SetLogFile(logWay string, logFile string, maxdays int64) {
if logWay == "console" {
Log.SetLogger("console", "")
} else {
params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays)
Log.SetLogger("file", params)
}
}
// value: error, warning, info, debug
func SetLogLevel(logLevel string) {
level := 4 // warning
switch logLevel {
case "error":
level = 3
case "warn":
level = 4
case "info":
level = 6
case "debug":
level = 7
default:
level = 4
}
Log.SetLevel(level)
}
// wrap log
func Error(format string, v ...interface{}) {
Log.Error(format, v...)
}
func Warn(format string, v ...interface{}) {
Log.Warn(format, v...)
}
func Info(format string, v ...interface{}) {
Log.Info(format, v...)
}
func Debug(format string, v ...interface{}) {
Log.Debug(format, v...)
}

View File

@@ -0,0 +1,107 @@
// 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 pcrypto
import (
"bytes"
"compress/gzip"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
)
type Pcrypto struct {
pkey []byte
paes cipher.Block
}
func (pc *Pcrypto) Init(key []byte) error {
var err error
pc.pkey = pKCS7Padding(key, aes.BlockSize)
pc.paes, err = aes.NewCipher(pc.pkey)
return err
}
func (pc *Pcrypto) Encrypt(src []byte) ([]byte, error) {
// aes
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
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)
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
}
func pKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func pKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func GetAuthKey(str string) (authKey string) {
md5Ctx := md5.New()
md5Ctx.Write([]byte(str))
md5Str := md5Ctx.Sum(nil)
return hex.EncodeToString(md5Str)
}

View File

@@ -0,0 +1,62 @@
// 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 pcrypto
import (
"testing"
)
var (
pp *Pcrypto
)
func init() {
pp = &Pcrypto{}
pp.Init([]byte("Hana"))
}
func TestEncrypt(t *testing.T) {
testStr := "Test Encrypt!"
res, err := pp.Encrypt([]byte(testStr))
if err != nil {
t.Fatalf("encrypt error: %v", err)
}
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 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))
}
}

58
src/utils/pool/pool.go Normal file
View File

@@ -0,0 +1,58 @@
// 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 pool
import "sync"
var (
bufPool5k sync.Pool
bufPool2k sync.Pool
bufPool1k sync.Pool
bufPool sync.Pool
)
func GetBuf(size int) []byte {
var x interface{}
if size >= 5*1024 {
x = bufPool5k.Get()
} else if size >= 2*1024 {
x = bufPool2k.Get()
} else if size >= 1*1024 {
x = bufPool1k.Get()
} else {
x = bufPool.Get()
}
if x == nil {
return make([]byte, size)
}
buf := x.([]byte)
if cap(buf) < size {
return make([]byte, size)
}
return buf[:size]
}
func PutBuf(buf []byte) {
size := cap(buf)
if size >= 5*1024 {
bufPool5k.Put(buf)
} else if size >= 2*1024 {
bufPool2k.Put(buf)
} else if size >= 1*1024 {
bufPool1k.Put(buf)
} else {
bufPool.Put(buf)
}
}

View File

@@ -0,0 +1,58 @@
// 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 version
import (
"strconv"
"strings"
)
var version string = "0.8.0"
func Full() string {
return version
}
func Proto(v string) int64 {
arr := strings.Split(v, ".")
if len(arr) < 2 {
return 0
}
res, _ := strconv.ParseInt(arr[0], 10, 64)
return res
}
func Major(v string) int64 {
arr := strings.Split(v, ".")
if len(arr) < 2 {
return 0
}
res, _ := strconv.ParseInt(arr[1], 10, 64)
return res
}
func Minor(v string) int64 {
arr := strings.Split(v, ".")
if len(arr) < 2 {
return 0
}
res, _ := strconv.ParseInt(arr[2], 10, 64)
return res
}
// add every case there if server will not accept client's protocol and return false
func Compat(client string, server string) bool {
return true
}

View File

@@ -0,0 +1,56 @@
// 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 version
import (
"fmt"
"strconv"
"strings"
"testing"
)
func TestFull(t *testing.T) {
version := Full()
arr := strings.Split(version, ".")
if len(arr) != 3 {
t.Fatalf("Version string error: %s", version)
}
proto, err := strconv.ParseInt(arr[0], 10, 64)
if err != nil || proto < 0 {
t.Fatalf("Version proto error")
}
major, err := strconv.ParseInt(arr[1], 10, 64)
if err != nil || major < 0 {
t.Fatalf("Version major error")
}
minor, err := strconv.ParseInt(arr[2], 10, 64)
if err != nil || minor < 0 {
t.Fatalf("Version minor error")
}
}
func TestVersion(t *testing.T) {
proto := Proto(Full())
major := Major(Full())
minor := Minor(Full())
parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor)
version := Full()
if parseVerion != version {
t.Fatalf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion)
}
}

171
src/utils/vhost/http.go Normal file
View File

@@ -0,0 +1,171 @@
// 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"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
"github.com/fatedier/frp/src/utils/conn"
"github.com/fatedier/frp/src/utils/pool"
)
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, HttpHostNameRewrite, timeout)
return &HttpMuxer{mux}, err
}
func HttpHostNameRewrite(c *conn.Conn, rewriteHost string) (_ net.Conn, err error) {
sc, rd := newShareConn(c.TcpConn)
var buff []byte
if buff, err = hostNameRewrite(rd, rewriteHost); err != nil {
return sc, err
}
err = sc.WriteBuff(buff)
return sc, err
}
func hostNameRewrite(request io.Reader, rewriteHost string) (_ []byte, err error) {
buf := pool.GetBuf(1024)
defer pool.PutBuf(buf)
request.Read(buf)
retBuffer, err := parseRequest(buf, rewriteHost)
return retBuffer, err
}
func parseRequest(org []byte, rewriteHost string) (ret []byte, err error) {
tp := bytes.NewBuffer(org)
// First line: GET /index.html HTTP/1.0
var b []byte
if b, err = tp.ReadBytes('\n'); err != nil {
return nil, err
}
req := new(http.Request)
// we invoked ReadRequest in GetHttpHostname before, so we ignore error
req.Method, req.RequestURI, req.Proto, _ = parseRequestLine(string(b))
rawurl := req.RequestURI
// CONNECT www.google.com:443 HTTP/1.1
justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/")
if justAuthority {
rawurl = "http://" + rawurl
}
req.URL, _ = url.ParseRequestURI(rawurl)
if justAuthority {
// Strip the bogus "http://" back off.
req.URL.Scheme = ""
}
// RFC2616: first case
// GET /index.html HTTP/1.1
// Host: www.google.com
if req.URL.Host == "" {
changedBuf, err := changeHostName(tp, rewriteHost)
buf := new(bytes.Buffer)
buf.Write(b)
buf.Write(changedBuf)
return buf.Bytes(), err
}
// RFC2616: second case
// GET http://www.google.com/index.html HTTP/1.1
// Host: doesntmatter
// In this case, any Host line is ignored.
hostPort := strings.Split(req.URL.Host, ":")
if len(hostPort) == 1 {
req.URL.Host = rewriteHost
} else if len(hostPort) == 2 {
req.URL.Host = fmt.Sprintf("%s:%s", rewriteHost, hostPort[1])
}
firstLine := req.Method + " " + req.URL.String() + " " + req.Proto
buf := new(bytes.Buffer)
buf.WriteString(firstLine)
tp.WriteTo(buf)
return buf.Bytes(), err
}
// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
s1 := strings.Index(line, " ")
s2 := strings.Index(line[s1+1:], " ")
if s1 < 0 || s2 < 0 {
return
}
s2 += s1 + 1
return line[:s1], line[s1+1 : s2], line[s2+1:], true
}
func changeHostName(buff *bytes.Buffer, rewriteHost string) (_ []byte, err error) {
retBuf := new(bytes.Buffer)
peek := buff.Bytes()
for len(peek) > 0 {
i := bytes.IndexByte(peek, '\n')
if i < 3 {
// Not present (-1) or found within the next few bytes,
// implying we're at the end ("\r\n\r\n" or "\n\n")
return nil, err
}
kv := peek[:i]
j := bytes.IndexByte(kv, ':')
if j < 0 {
return nil, fmt.Errorf("malformed MIME header line: " + string(kv))
}
if strings.Contains(strings.ToLower(string(kv[:j])), "host") {
var hostHeader string
portPos := bytes.IndexByte(kv[j+1:], ':')
if portPos == -1 {
hostHeader = fmt.Sprintf("Host: %s\n", rewriteHost)
} else {
hostHeader = fmt.Sprintf("Host: %s:%s\n", rewriteHost, kv[portPos+1:])
}
retBuf.WriteString(hostHeader)
peek = peek[i+1:]
break
} else {
retBuf.Write(peek[:i])
retBuf.WriteByte('\n')
}
peek = peek[i+1:]
}
retBuf.Write(peek)
return retBuf.Bytes(), err
}

188
src/utils/vhost/https.go Normal file
View File

@@ -0,0 +1,188 @@
// 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 (
"fmt"
"io"
"net"
"strings"
"time"
"github.com/fatedier/frp/src/utils/conn"
"github.com/fatedier/frp/src/utils/pool"
)
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
}
func NewHttpsMuxer(listener *conn.Listener, timeout time.Duration) (*HttpsMuxer, error) {
mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, timeout)
return &HttpsMuxer{mux}, err
}
func readHandshake(rd io.Reader) (host string, err error) {
data := pool.GetBuf(1024)
origin := data
defer pool.PutBuf(origin)
length, err := rd.Read(data)
if err != nil {
return
} else {
if length < 47 {
err = fmt.Errorf("readHandshake: proto length[%d] is too short", length)
return
}
}
data = data[:length]
if uint8(data[5]) != typeClientHello {
err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5]))
return
}
// session
sessionIdLen := int(data[43])
if sessionIdLen > 32 || len(data) < 44+sessionIdLen {
err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIdLen)
return
}
data = data[44+sessionIdLen:]
if len(data) < 2 {
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 {
err = fmt.Errorf("readHandshake: dataLen[%d] after cipher suite is short", len(data))
return
}
data = data[2+cipherSuiteLen:]
if len(data) < 1 {
err = fmt.Errorf("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen)
return
}
// compression method
compressionMethodsLen := int(data[0])
if len(data) < 1+compressionMethodsLen {
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
err = fmt.Errorf("readHandshake: there is no extension data to get servername")
return
}
if len(data) < 2 {
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) {
err = fmt.Errorf("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data))
return
}
for len(data) != 0 {
if len(data) < 4 {
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 {
err = fmt.Errorf("readHandshake: extensionLen[%d] is long", length)
return
}
switch extension {
case extensionRenegotiationInfo:
if length != 1 || data[0] != 0 {
err = fmt.Errorf("readHandshake: extension reNegotiationInfoLen[%d] is short", length)
return
}
case extensionNextProtoNeg:
case extensionStatusRequest:
case extensionServerName:
d := data[:length]
if len(d) < 2 {
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 {
err = fmt.Errorf("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d))
return
}
for len(d) > 0 {
if len(d) < 3 {
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 {
err = fmt.Errorf("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d))
return
}
if nameType == 0 {
serverName := string(d[:nameLen])
host = strings.TrimSpace(serverName)
return host, nil
}
d = d[nameLen:]
}
}
data = data[length:]
}
err = fmt.Errorf("Unknow error")
return
}
func GetHttpsHostname(c *conn.Conn) (sc net.Conn, routerName string, err error) {
sc, rd := newShareConn(c.TcpConn)
host, err := readHandshake(rd)
if err != nil {
return sc, "", err
}
return sc, host, nil
}

191
src/utils/vhost/vhost.go Normal file
View File

@@ -0,0 +1,191 @@
// 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 (
"bytes"
"fmt"
"io"
"net"
"strings"
"sync"
"time"
"github.com/fatedier/frp/src/utils/conn"
)
type muxFunc func(*conn.Conn) (net.Conn, string, error)
type hostRewriteFunc func(*conn.Conn, string) (net.Conn, error)
type VhostMuxer struct {
listener *conn.Listener
timeout time.Duration
vhostFunc muxFunc
rewriteFunc hostRewriteFunc
registryMap map[string]*Listener
mutex sync.RWMutex
}
func NewVhostMuxer(listener *conn.Listener, vhostFunc muxFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
mux = &VhostMuxer{
listener: listener,
timeout: timeout,
vhostFunc: vhostFunc,
rewriteFunc: rewriteFunc,
registryMap: make(map[string]*Listener),
}
go mux.run()
return mux, nil
}
// listen for a new domain name, if rewriteHost is not empty and rewriteFunc is not nil, then rewrite the host header to rewriteHost
func (v *VhostMuxer) Listen(name string, rewriteHost string) (l *Listener, err error) {
v.mutex.Lock()
defer v.mutex.Unlock()
if _, exist := v.registryMap[name]; exist {
return nil, fmt.Errorf("domain name %s is already bound", name)
}
l = &Listener{
name: name,
rewriteHost: rewriteHost,
mux: v,
accept: make(chan *conn.Conn),
}
v.registryMap[name] = l
return l, nil
}
func (v *VhostMuxer) getListener(name string) (l *Listener, exist bool) {
v.mutex.RLock()
defer v.mutex.RUnlock()
l, exist = v.registryMap[name]
return l, exist
}
func (v *VhostMuxer) unRegister(name string) {
v.mutex.Lock()
defer v.mutex.Unlock()
delete(v.registryMap, name)
}
func (v *VhostMuxer) run() {
for {
conn, err := v.listener.Accept()
if err != nil {
return
}
go v.handle(conn)
}
}
func (v *VhostMuxer) handle(c *conn.Conn) {
if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
return
}
sConn, name, err := v.vhostFunc(c)
if err != nil {
return
}
name = strings.ToLower(name)
l, ok := v.getListener(name)
if !ok {
return
}
if err = sConn.SetDeadline(time.Time{}); err != nil {
return
}
c.SetTcpConn(sConn)
l.accept <- c
}
type Listener struct {
name string
rewriteHost string
mux *VhostMuxer // for closing VhostMuxer
accept chan *conn.Conn
}
func (l *Listener) Accept() (*conn.Conn, error) {
conn, ok := <-l.accept
if !ok {
return nil, fmt.Errorf("Listener closed")
}
// if rewriteFunc is exist and rewriteHost is set
// rewrite http requests with a modified host header
if l.mux.rewriteFunc != nil && l.rewriteHost != "" {
sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
if err != nil {
return nil, fmt.Errorf("http host header rewrite failed")
}
conn.SetTcpConn(sConn)
}
return conn, nil
}
func (l *Listener) Close() error {
l.mux.unRegister(l.name)
close(l.accept)
return nil
}
func (l *Listener) Name() string {
return l.name
}
type sharedConn struct {
net.Conn
sync.Mutex
buff *bytes.Buffer
}
// the bytes you read in io.Reader, will be reserved in sharedConn
func newShareConn(conn net.Conn) (*sharedConn, io.Reader) {
sc := &sharedConn{
Conn: conn,
buff: bytes.NewBuffer(make([]byte, 0, 1024)),
}
return sc, io.TeeReader(conn, sc.buff)
}
func (sc *sharedConn) Read(p []byte) (n int, err error) {
sc.Lock()
if sc.buff == nil {
sc.Unlock()
return sc.Conn.Read(p)
}
n, err = sc.buff.Read(p)
if err == io.EOF {
sc.buff = nil
var n2 int
n2, err = sc.Conn.Read(p[n:])
n += n2
}
sc.Unlock()
return
}
func (sc *sharedConn) WriteBuff(buffer []byte) (err error) {
sc.buff.Reset()
_, err = sc.buff.Write(buffer)
return err
}