├── .dockerignore ├── vendor └── github.com │ ├── vaughan0 │ └── go-ini │ │ ├── test.ini │ │ ├── LICENSE │ │ ├── README.md │ │ └── ini.go │ ├── stretchr │ └── testify │ │ ├── assert │ │ ├── assertion_forward.go.tmpl │ │ ├── errors.go │ │ ├── forward_assertions.go │ │ ├── doc.go │ │ └── http_assertions.go │ │ ├── LICENSE │ │ └── LICENCE.txt │ ├── docopt │ └── docopt-go │ │ ├── test_golang.docopt │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ └── README.md │ ├── astaxie │ └── beego │ │ ├── LICENSE │ │ └── logs │ │ ├── color.go │ │ ├── README.md │ │ ├── console.go │ │ ├── conn.go │ │ ├── multifile.go │ │ ├── smtp.go │ │ └── logger.go │ ├── davecgh │ └── go-spew │ │ ├── LICENSE │ │ └── spew │ │ ├── bypasssafe.go │ │ ├── bypass.go │ │ ├── spew.go │ │ └── doc.go │ ├── pmezard │ └── go-difflib │ │ └── LICENSE │ └── rakyll │ └── statik │ └── fs │ └── fs.go ├── doc ├── pic │ ├── dashboard.png │ ├── architecture.png │ └── donate-alipay.png ├── quick_start_zh.md └── quick_start_en.md ├── src ├── assets │ ├── static │ │ ├── favicon.ico │ │ ├── font │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.svg │ │ └── css │ │ │ └── iconfont.css │ └── assets.go ├── utils │ ├── conn │ │ ├── udp_conn.go │ │ └── conn.go │ ├── pool │ │ └── pool.go │ ├── version │ │ ├── version.go │ │ └── version_test.go │ ├── broadcast │ │ ├── broadcast_test.go │ │ └── broadcast.go │ ├── pcrypto │ │ ├── pcrypto_test.go │ │ └── pcrypto.go │ ├── log │ │ └── log.go │ └── vhost │ │ ├── router.go │ │ ├── https.go │ │ ├── vhost.go │ │ └── http.go ├── models │ ├── consts │ │ └── consts.go │ ├── config │ │ └── config.go │ ├── server │ │ ├── dashboard_view.go │ │ ├── dashboard_api.go │ │ └── dashboard.go │ ├── msg │ │ ├── udp_test.go │ │ ├── msg.go │ │ ├── udp.go │ │ └── process.go │ ├── client │ │ ├── process_udp.go │ │ ├── client.go │ │ └── config.go │ └── metric │ │ └── server.go └── cmd │ ├── frpc │ ├── main.go │ └── control.go │ └── frps │ └── main.go ├── Godeps ├── Readme └── Godeps.json ├── .travis.yml ├── conf ├── frpc_min.ini ├── frps_min.ini ├── frps.ini └── frpc.ini ├── Dockerfile_alpine ├── test ├── conf │ ├── auto_test_frps.ini │ └── auto_test_frpc.ini ├── http_server.go ├── clean_test.sh ├── run_test.sh ├── echo_server.go └── func_test.go ├── Dockerfile ├── .gitignore ├── Makefile ├── Makefile.cross-compiles └── package.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .git 3 | *~ 4 | *# 5 | .#* 6 | -------------------------------------------------------------------------------- /vendor/github.com/vaughan0/go-ini/test.ini: -------------------------------------------------------------------------------- 1 | [default] 2 | stuff = things 3 | -------------------------------------------------------------------------------- /doc/pic/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/doc/pic/dashboard.png -------------------------------------------------------------------------------- /doc/pic/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/doc/pic/architecture.png -------------------------------------------------------------------------------- /doc/pic/donate-alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/doc/pic/donate-alipay.png -------------------------------------------------------------------------------- /src/assets/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/src/assets/static/favicon.ico -------------------------------------------------------------------------------- /src/assets/static/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/src/assets/static/font/iconfont.eot -------------------------------------------------------------------------------- /src/assets/static/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/src/assets/static/font/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/static/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monkey-wenjun/frp/master/src/assets/static/font/iconfont.woff -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | 4 | go: 5 | - 1.5.4 6 | - 1.6.4 7 | - 1.7.4 8 | 9 | install: 10 | - make 11 | 12 | script: 13 | - make alltest 14 | -------------------------------------------------------------------------------- /conf/frpc_min.ini: -------------------------------------------------------------------------------- 1 | [common] 2 | server_addr = 0.0.0.0 3 | server_port = 7000 4 | auth_token = 123 5 | privilege_token = 12345678 6 | 7 | [ssh] 8 | type = tcp 9 | local_ip = 127.0.0.1 10 | local_port = 22 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 4 | } 5 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt-go/test_golang.docopt: -------------------------------------------------------------------------------- 1 | r"""usage: prog [NAME_-2]...""" 2 | $ prog 10 20 3 | {"NAME_-2": ["10", "20"]} 4 | 5 | $ prog 10 6 | {"NAME_-2": ["10"]} 7 | 8 | $ prog 9 | {"NAME_-2": []} 10 | -------------------------------------------------------------------------------- /Dockerfile_alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | 3 | COPY bin/frpc /frpc 4 | COPY bin/frps /frps 5 | COPY conf/frpc_min.ini /frpc.ini 6 | COPY conf/frps_min.ini /frps.ini 7 | 8 | WORKDIR / 9 | 10 | EXPOSE 80 443 6000 7000 7500 11 | 12 | ENTRYPOINT ["/frps"] 13 | -------------------------------------------------------------------------------- /conf/frps_min.ini: -------------------------------------------------------------------------------- 1 | [common] 2 | bind_addr = 0.0.0.0 3 | bind_port = 7000 4 | vhost_http_port = 80 5 | vhost_https_port = 443 6 | dashboard_port = 7500 7 | privilege_mode = true 8 | privilege_token = 12345678 9 | 10 | [ssh] 11 | type = tcp 12 | auth_token = 123 13 | bind_addr = 0.0.0.0 14 | listen_port = 6000 15 | -------------------------------------------------------------------------------- /test/conf/auto_test_frps.ini: -------------------------------------------------------------------------------- 1 | [common] 2 | bind_addr = 0.0.0.0 3 | bind_port = 10700 4 | vhost_http_port = 10710 5 | log_file = ./frps.log 6 | log_level = debug 7 | 8 | [echo] 9 | type = tcp 10 | auth_token = 123 11 | bind_addr = 0.0.0.0 12 | listen_port = 10711 13 | 14 | [web] 15 | type = http 16 | auth_token = 123 17 | custom_domains = 127.0.0.1 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.6 2 | 3 | COPY . /go/src/github.com/fatedier/frp 4 | 5 | RUN cd /go/src/github.com/fatedier/frp \ 6 | && make \ 7 | && mv bin/frpc /frpc \ 8 | && mv bin/frps /frps \ 9 | && mv conf/frpc_min.ini /frpc.ini \ 10 | && mv conf/frps_min.ini /frps.ini \ 11 | && make clean 12 | 13 | WORKDIR / 14 | 15 | EXPOSE 80 443 6000 7000 7500 16 | 17 | ENTRYPOINT ["/frps"] 18 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /test/conf/auto_test_frpc.ini: -------------------------------------------------------------------------------- 1 | [common] 2 | server_addr = 0.0.0.0 3 | server_port = 10700 4 | log_file = ./frpc.log 5 | # debug, info, warn, error 6 | log_level = debug 7 | auth_token = 123 8 | 9 | [echo] 10 | type = tcp 11 | local_ip = 127.0.0.1 12 | local_port = 10701 13 | use_encryption = true 14 | use_gzip = true 15 | 16 | [web] 17 | type = http 18 | local_ip = 127.0.0.1 19 | local_port = 10702 20 | use_gzip = true 21 | -------------------------------------------------------------------------------- /test/http_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | var ( 9 | PORT int64 = 10702 10 | HTTP_RES_STR string = "Hello World" 11 | ) 12 | 13 | func main() { 14 | http.HandleFunc("/", request) 15 | http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", PORT), nil) 16 | } 17 | 18 | func request(w http.ResponseWriter, r *http.Request) { 19 | w.Write([]byte(HTTP_RES_STR)) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt-go/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | # coverage droppings 25 | profile.cov 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Self 27 | bin/ 28 | packages/ 29 | test/bin/ 30 | 31 | # Cache 32 | *.swp 33 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl 17 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 astaxie 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /test/clean_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pid=`ps aux|grep './bin/echo_server'|grep -v grep|awk {'print $2'}` 4 | if [ -n "${pid}" ]; then 5 | kill ${pid} 6 | fi 7 | 8 | pid=`ps aux|grep './bin/http_server'|grep -v grep|awk {'print $2'}` 9 | if [ -n "${pid}" ]; then 10 | kill ${pid} 11 | fi 12 | 13 | pid=`ps aux|grep './../bin/frps -c ./conf/auto_test_frps.ini'|grep -v grep|awk {'print $2'}` 14 | if [ -n "${pid}" ]; then 15 | kill ${pid} 16 | fi 17 | 18 | pid=`ps aux|grep './../bin/frpc -c ./conf/auto_test_frpc.ini'|grep -v grep|awk {'print $2'}` 19 | if [ -n "${pid}" ]; then 20 | kill ${pid} 21 | fi 22 | 23 | rm -f ./frps.log 24 | rm -f ./frpc.log 25 | -------------------------------------------------------------------------------- /test/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./bin/echo_server & 4 | ./bin/http_server & 5 | ./../bin/frps -c ./conf/auto_test_frps.ini & 6 | sleep 1 7 | ./../bin/frpc -c ./conf/auto_test_frpc.ini & 8 | 9 | # wait until proxies are connected 10 | for((i=1; i<15; i++)) 11 | do 12 | sleep 1 13 | str=`ss -ant|grep 10700|grep LISTEN` 14 | if [ -z "${str}" ]; then 15 | echo "wait" 16 | continue 17 | fi 18 | 19 | str=`ss -ant|grep 10710|grep LISTEN` 20 | if [ -z "${str}" ]; then 21 | echo "wait" 22 | continue 23 | fi 24 | 25 | str=`ss -ant|grep 10711|grep LISTEN` 26 | if [ -z "${str}" ]; then 27 | echo "wait" 28 | continue 29 | fi 30 | 31 | break 32 | done 33 | 34 | sleep 1 35 | -------------------------------------------------------------------------------- /src/assets/static/css/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('/static/font/iconfont.eot'); /* IE9*/ 4 | src: url('/static/font/iconfont.eot#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('/static/font/iconfont.woff') format('woff'), /* chrome, firefox */ 6 | url('/static/font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('/static/font/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -webkit-text-stroke-width: 0.2px; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | .icon-sort:before { content: "\e66d"; } 19 | -------------------------------------------------------------------------------- /test/echo_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/fatedier/frp/src/utils/conn" 8 | ) 9 | 10 | var ( 11 | PORT int64 = 10701 12 | ) 13 | 14 | func main() { 15 | l, err := conn.Listen("0.0.0.0", PORT) 16 | if err != nil { 17 | fmt.Printf("echo server listen error: %v\n", err) 18 | return 19 | } 20 | 21 | for { 22 | c, err := l.Accept() 23 | if err != nil { 24 | fmt.Printf("echo server accept error: %v\n", err) 25 | return 26 | } 27 | 28 | go echoWorker(c) 29 | } 30 | } 31 | 32 | func echoWorker(c *conn.Conn) { 33 | for { 34 | buff, err := c.ReadLine() 35 | if err == io.EOF { 36 | break 37 | } 38 | if err != nil { 39 | fmt.Printf("echo server read error: %v\n", err) 40 | return 41 | } 42 | 43 | c.WriteString(buff) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt-go/.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI (http://travis-ci.org/) is a continuous integration 2 | # service for open source projects. This file configures it 3 | # to run unit tests for docopt-go. 4 | 5 | language: go 6 | 7 | go: 8 | - 1.4 9 | - 1.5 10 | - tip 11 | 12 | matrix: 13 | fast_finish: true 14 | 15 | before_install: 16 | - go get golang.org/x/tools/cmd/vet 17 | - go get golang.org/x/tools/cmd/cover 18 | - go get github.com/golang/lint/golint 19 | - go get github.com/mattn/goveralls 20 | 21 | install: 22 | - go get -d -v ./... && go build -v ./... 23 | 24 | script: 25 | - go vet -x ./... 26 | - $HOME/gopath/bin/golint ./... 27 | - go test -v ./... 28 | - go test -covermode=count -coverprofile=profile.cov . 29 | 30 | after_script: 31 | - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci 32 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/color.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +build !windows 16 | 17 | package logs 18 | 19 | import "io" 20 | 21 | type ansiColorWriter struct { 22 | w io.Writer 23 | mode outputMode 24 | } 25 | 26 | func (cw *ansiColorWriter) Write(p []byte) (int, error) { 27 | return cw.w.Write(p) 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/conn/udp_conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package conn 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | ) 21 | 22 | func ListenUDP(bindAddr string, bindPort int64) (conn *net.UDPConn, err error) { 23 | udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort)) 24 | if err != nil { 25 | return conn, err 26 | } 27 | conn, err = net.ListenUDP("udp", udpAddr) 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /src/models/consts/consts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package consts 16 | 17 | // server status 18 | const ( 19 | Idle = iota 20 | Working 21 | Closed 22 | ) 23 | 24 | var ( 25 | StatusStr = []string{ 26 | "idle", 27 | "working", 28 | "closed", 29 | } 30 | ) 31 | 32 | // msg type 33 | const ( 34 | NewCtlConn = iota 35 | NewWorkConn 36 | NoticeUserConn 37 | NewCtlConnRes 38 | HeartbeatReq 39 | HeartbeatRes 40 | NewWorkConnUdp 41 | ) 42 | -------------------------------------------------------------------------------- /src/models/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | type BaseConf struct { 18 | Name string 19 | AuthToken string 20 | Type string 21 | UseEncryption bool 22 | UseGzip bool 23 | PrivilegeMode bool 24 | PrivilegeToken string 25 | PoolCount int64 26 | HostHeaderRewrite string 27 | HttpUserName string 28 | HttpPassWord string 29 | SubDomain string 30 | } 31 | -------------------------------------------------------------------------------- /vendor/github.com/vaughan0/go-ini/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Vaughan Newton 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 6 | persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt-go/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Keith Batten 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/fatedier/frp", 3 | "GoVersion": "go1.7", 4 | "GodepVersion": "v75", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/astaxie/beego/logs", 11 | "Comment": "v1.7.0-7-gefbde1e", 12 | "Rev": "efbde1ee77517486eac03e814e01d724ddad18e6" 13 | }, 14 | { 15 | "ImportPath": "github.com/davecgh/go-spew/spew", 16 | "Comment": "v1.1.0", 17 | "Rev": "346938d642f2ec3594ed81d874461961cd0faa76" 18 | }, 19 | { 20 | "ImportPath": "github.com/docopt/docopt-go", 21 | "Comment": "0.6.2", 22 | "Rev": "784ddc588536785e7299f7272f39101f7faccc3f" 23 | }, 24 | { 25 | "ImportPath": "github.com/pmezard/go-difflib/difflib", 26 | "Comment": "v1.0.0", 27 | "Rev": "792786c7400a136282c1664665ae0a8db921c6c2" 28 | }, 29 | { 30 | "ImportPath": "github.com/rakyll/statik/fs", 31 | "Comment": "v0.1.0", 32 | "Rev": "274df120e9065bdd08eb1120e0375e3dc1ae8465" 33 | }, 34 | { 35 | "ImportPath": "github.com/stretchr/testify/assert", 36 | "Comment": "v1.1.4-25-g2402e8e", 37 | "Rev": "2402e8e7a02fc811447d11f881aa9746cdc57983" 38 | }, 39 | { 40 | "ImportPath": "github.com/vaughan0/go-ini", 41 | "Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export PATH := $(GOPATH)/bin:$(PATH) 2 | export GO15VENDOREXPERIMENT := 1 3 | 4 | all: fmt build 5 | 6 | build: frps frpc build_test 7 | 8 | build_test: echo_server http_server 9 | 10 | # compile assets into binary file 11 | assets: 12 | go get -d github.com/rakyll/statik 13 | @go install github.com/rakyll/statik 14 | @rm -rf ./src/assets/statik 15 | go generate ./src/... 16 | 17 | fmt: 18 | go fmt ./src/... 19 | @go fmt ./test/echo_server.go 20 | @go fmt ./test/http_server.go 21 | @go fmt ./test/func_test.go 22 | 23 | frps: 24 | go build -o bin/frps ./src/cmd/frps 25 | @cp -rf ./src/assets/static ./bin 26 | 27 | frpc: 28 | go build -o bin/frpc ./src/cmd/frpc 29 | 30 | echo_server: 31 | go build -o test/bin/echo_server ./test/echo_server.go 32 | 33 | http_server: 34 | go build -o test/bin/http_server ./test/http_server.go 35 | 36 | test: gotest 37 | 38 | gotest: 39 | go test -v ./src/... 40 | 41 | alltest: 42 | cd ./test && ./run_test.sh && cd - 43 | go test -v ./src/... 44 | go test -v ./test/func_test.go 45 | cd ./test && ./clean_test.sh && cd - 46 | 47 | clean: 48 | rm -f ./bin/frpc 49 | rm -f ./bin/frps 50 | rm -f ./test/bin/echo_server 51 | rm -f ./test/bin/http_server 52 | cd ./test && ./clean_test.sh && cd - 53 | 54 | save: 55 | godep save ./src/... 56 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/README.md: -------------------------------------------------------------------------------- 1 | ## logs 2 | logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` . 3 | 4 | 5 | ## How to install? 6 | 7 | go get github.com/astaxie/beego/logs 8 | 9 | 10 | ## What adapters are supported? 11 | 12 | As of now this logs support console, file,smtp and conn. 13 | 14 | 15 | ## How to use it? 16 | 17 | First you must import it 18 | 19 | import ( 20 | "github.com/astaxie/beego/logs" 21 | ) 22 | 23 | Then init a Log (example with console adapter) 24 | 25 | log := NewLogger(10000) 26 | log.SetLogger("console", "") 27 | 28 | > the first params stand for how many channel 29 | 30 | Use it like this: 31 | 32 | log.Trace("trace") 33 | log.Info("info") 34 | log.Warn("warning") 35 | log.Debug("debug") 36 | log.Critical("critical") 37 | 38 | 39 | ## File adapter 40 | 41 | Configure file adapter like this: 42 | 43 | log := NewLogger(10000) 44 | log.SetLogger("file", `{"filename":"test.log"}`) 45 | 46 | 47 | ## Conn adapter 48 | 49 | Configure like this: 50 | 51 | log := NewLogger(1000) 52 | log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`) 53 | log.Info("info") 54 | 55 | 56 | ## Smtp adapter 57 | 58 | Configure like this: 59 | 60 | log := NewLogger(10000) 61 | log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`) 62 | log.Critical("sendmail critical") 63 | time.Sleep(time.Second * 30) 64 | -------------------------------------------------------------------------------- /src/models/server/dashboard_view.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "html/template" 19 | "net/http" 20 | 21 | "github.com/fatedier/frp/src/assets" 22 | "github.com/fatedier/frp/src/models/metric" 23 | "github.com/fatedier/frp/src/utils/log" 24 | ) 25 | 26 | func viewDashboard(w http.ResponseWriter, r *http.Request) { 27 | metrics := metric.GetAllProxyMetrics() 28 | dashboardTpl, err := assets.ReadFile("index.html") 29 | if err != nil { 30 | http.Error(w, "get dashboard template file error", http.StatusInternalServerError) 31 | return 32 | } 33 | t := template.Must(template.New("index.html").Delims("<<<", ">>>").Parse(dashboardTpl)) 34 | 35 | err = t.Execute(w, metrics) 36 | if err != nil { 37 | log.Warn("parse template file [index.html] error: %v", err) 38 | http.Error(w, "parse template file error", http.StatusInternalServerError) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/vaughan0/go-ini/README.md: -------------------------------------------------------------------------------- 1 | go-ini 2 | ====== 3 | 4 | INI parsing library for Go (golang). 5 | 6 | View the API documentation [here](http://godoc.org/github.com/vaughan0/go-ini). 7 | 8 | Usage 9 | ----- 10 | 11 | Parse an INI file: 12 | 13 | ```go 14 | import "github.com/vaughan0/go-ini" 15 | 16 | file, err := ini.LoadFile("myfile.ini") 17 | ``` 18 | 19 | Get data from the parsed file: 20 | 21 | ```go 22 | name, ok := file.Get("person", "name") 23 | if !ok { 24 | panic("'name' variable missing from 'person' section") 25 | } 26 | ``` 27 | 28 | Iterate through values in a section: 29 | 30 | ```go 31 | for key, value := range file["mysection"] { 32 | fmt.Printf("%s => %s\n", key, value) 33 | } 34 | ``` 35 | 36 | Iterate through sections in a file: 37 | 38 | ```go 39 | for name, section := range file { 40 | fmt.Printf("Section name: %s\n", name) 41 | } 42 | ``` 43 | 44 | File Format 45 | ----------- 46 | 47 | INI files are parsed by go-ini line-by-line. Each line may be one of the following: 48 | 49 | * A section definition: [section-name] 50 | * A property: key = value 51 | * A comment: #blahblah _or_ ;blahblah 52 | * Blank. The line will be ignored. 53 | 54 | Properties defined before any section headers are placed in the default section, which has 55 | the empty string as it's key. 56 | 57 | Example: 58 | 59 | ```ini 60 | # I am a comment 61 | ; So am I! 62 | 63 | [apples] 64 | colour = red or green 65 | shape = applish 66 | 67 | [oranges] 68 | shape = square 69 | colour = blue 70 | ``` 71 | -------------------------------------------------------------------------------- /Makefile.cross-compiles: -------------------------------------------------------------------------------- 1 | export PATH := $(GOPATH)/bin:$(PATH) 2 | export GO15VENDOREXPERIMENT := 1 3 | 4 | all: build 5 | 6 | build: app 7 | 8 | app: 9 | env GOOS=darwin GOARCH=386 go build -o ./frpc_darwin_386 ./src/cmd/frpc 10 | env GOOS=darwin GOARCH=386 go build -o ./frps_darwin_386 ./src/cmd/frps 11 | env GOOS=darwin GOARCH=amd64 go build -o ./frpc_darwin_amd64 ./src/cmd/frpc 12 | env GOOS=darwin GOARCH=amd64 go build -o ./frps_darwin_amd64 ./src/cmd/frps 13 | env GOOS=linux GOARCH=386 go build -o ./frpc_linux_386 ./src/cmd/frpc 14 | env GOOS=linux GOARCH=386 go build -o ./frps_linux_386 ./src/cmd/frps 15 | env GOOS=linux GOARCH=amd64 go build -o ./frpc_linux_amd64 ./src/cmd/frpc 16 | env GOOS=linux GOARCH=amd64 go build -o ./frps_linux_amd64 ./src/cmd/frps 17 | env GOOS=linux GOARCH=arm go build -o ./frpc_linux_arm ./src/cmd/frpc 18 | env GOOS=linux GOARCH=arm go build -o ./frps_linux_arm ./src/cmd/frps 19 | env GOOS=windows GOARCH=386 go build -o ./frpc_windows_386.exe ./src/cmd/frpc 20 | env GOOS=windows GOARCH=386 go build -o ./frps_windows_386.exe ./src/cmd/frps 21 | env GOOS=windows GOARCH=amd64 go build -o ./frpc_windows_amd64.exe ./src/cmd/frpc 22 | env GOOS=windows GOARCH=amd64 go build -o ./frps_windows_amd64.exe ./src/cmd/frps 23 | env GOOS=linux GOARCH=mips64 go build -o ./frpc_linux_mips64 ./src/cmd/frpc 24 | env GOOS=linux GOARCH=mips64 go build -o ./frps_linux_mips64 ./src/cmd/frps 25 | env GOOS=linux GOARCH=mips64le go build -o ./frpc_linux_mips64le ./src/cmd/frpc 26 | env GOOS=linux GOARCH=mips64le go build -o ./frps_linux_mips64le ./src/cmd/frps 27 | -------------------------------------------------------------------------------- /src/utils/pool/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pool 16 | 17 | import "sync" 18 | 19 | var ( 20 | bufPool5k sync.Pool 21 | bufPool2k sync.Pool 22 | bufPool1k sync.Pool 23 | bufPool sync.Pool 24 | ) 25 | 26 | func GetBuf(size int) []byte { 27 | var x interface{} 28 | if size >= 5*1024 { 29 | x = bufPool5k.Get() 30 | } else if size >= 2*1024 { 31 | x = bufPool2k.Get() 32 | } else if size >= 1*1024 { 33 | x = bufPool1k.Get() 34 | } else { 35 | x = bufPool.Get() 36 | } 37 | if x == nil { 38 | return make([]byte, size) 39 | } 40 | buf := x.([]byte) 41 | if cap(buf) < size { 42 | return make([]byte, size) 43 | } 44 | return buf[:size] 45 | } 46 | 47 | func PutBuf(buf []byte) { 48 | size := cap(buf) 49 | if size >= 5*1024 { 50 | bufPool5k.Put(buf) 51 | } else if size >= 2*1024 { 52 | bufPool2k.Put(buf) 53 | } else if size >= 1*1024 { 54 | bufPool1k.Put(buf) 55 | } else { 56 | bufPool.Put(buf) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/utils/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "strconv" 19 | "strings" 20 | ) 21 | 22 | var version string = "0.9.3" 23 | 24 | func Full() string { 25 | return version 26 | } 27 | 28 | func Proto(v string) int64 { 29 | arr := strings.Split(v, ".") 30 | if len(arr) < 2 { 31 | return 0 32 | } 33 | res, _ := strconv.ParseInt(arr[0], 10, 64) 34 | return res 35 | } 36 | 37 | func Major(v string) int64 { 38 | arr := strings.Split(v, ".") 39 | if len(arr) < 2 { 40 | return 0 41 | } 42 | res, _ := strconv.ParseInt(arr[1], 10, 64) 43 | return res 44 | } 45 | 46 | func Minor(v string) int64 { 47 | arr := strings.Split(v, ".") 48 | if len(arr) < 2 { 49 | return 0 50 | } 51 | res, _ := strconv.ParseInt(arr[2], 10, 64) 52 | return res 53 | } 54 | 55 | // add every case there if server will not accept client's protocol and return false 56 | func Compat(client string, server string) bool { 57 | return true 58 | } 59 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /src/models/msg/udp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package msg 16 | 17 | import ( 18 | "net" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | var ( 25 | content string = "udp packet test" 26 | src string = "1.1.1.1:1000" 27 | dst string = "2.2.2.2:2000" 28 | 29 | udpMsg *UdpPacket 30 | ) 31 | 32 | func init() { 33 | srcAddr, _ := net.ResolveUDPAddr("udp", src) 34 | dstAddr, _ := net.ResolveUDPAddr("udp", dst) 35 | udpMsg = NewUdpPacket([]byte(content), srcAddr, dstAddr) 36 | } 37 | 38 | func TestPack(t *testing.T) { 39 | assert := assert.New(t) 40 | msg := udpMsg.Pack() 41 | assert.Equal(string(msg), `{"content":"dWRwIHBhY2tldCB0ZXN0","src":"1.1.1.1:1000","dst":"2.2.2.2:2000"}`) 42 | } 43 | 44 | func TestUnpack(t *testing.T) { 45 | assert := assert.New(t) 46 | udpMsg.UnPack([]byte(`{"content":"dWRwIHBhY2tldCB0ZXN0","src":"1.1.1.1:1000","dst":"2.2.2.2:2000"}`)) 47 | assert.Equal(content, string(udpMsg.Content)) 48 | assert.Equal(src, udpMsg.Src.String()) 49 | assert.Equal(dst, udpMsg.Dst.String()) 50 | } 51 | -------------------------------------------------------------------------------- /test/func_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "strings" 8 | "testing" 9 | "time" 10 | 11 | "github.com/fatedier/frp/src/utils/conn" 12 | ) 13 | 14 | var ( 15 | ECHO_PORT int64 = 10711 16 | HTTP_PORT int64 = 10710 17 | ECHO_TEST_STR string = "Hello World\n" 18 | HTTP_RES_STR string = "Hello World" 19 | ) 20 | 21 | func TestEchoServer(t *testing.T) { 22 | c, err := conn.ConnectServer(fmt.Sprintf("0.0.0.0:%d", ECHO_PORT)) 23 | if err != nil { 24 | t.Fatalf("connect to echo server error: %v", err) 25 | } 26 | timer := time.Now().Add(time.Duration(5) * time.Second) 27 | c.SetDeadline(timer) 28 | 29 | c.WriteString(ECHO_TEST_STR) 30 | 31 | buff, err := c.ReadLine() 32 | if err != nil { 33 | t.Fatalf("read from echo server error: %v", err) 34 | } 35 | 36 | if ECHO_TEST_STR != buff { 37 | t.Fatalf("content error, send [%s], get [%s]", strings.Trim(ECHO_TEST_STR, "\n"), strings.Trim(buff, "\n")) 38 | } 39 | } 40 | 41 | func TestHttpServer(t *testing.T) { 42 | client := &http.Client{} 43 | req, _ := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:%d", HTTP_PORT), nil) 44 | res, err := client.Do(req) 45 | if err != nil { 46 | t.Fatalf("do http request error: %v", err) 47 | } 48 | if res.StatusCode == 200 { 49 | body, err := ioutil.ReadAll(res.Body) 50 | if err != nil { 51 | t.Fatalf("read from http server error: %v", err) 52 | } 53 | bodystr := string(body) 54 | if bodystr != HTTP_RES_STR { 55 | t.Fatalf("content from http server error [%s], correct string is [%s]", bodystr, HTTP_RES_STR) 56 | } 57 | } else { 58 | t.Fatalf("http code from http server error [%d]", res.StatusCode) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/version/version_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | "strings" 21 | "testing" 22 | ) 23 | 24 | func TestFull(t *testing.T) { 25 | version := Full() 26 | arr := strings.Split(version, ".") 27 | if len(arr) != 3 { 28 | t.Fatalf("Version string error: %s", version) 29 | } 30 | 31 | proto, err := strconv.ParseInt(arr[0], 10, 64) 32 | if err != nil || proto < 0 { 33 | t.Fatalf("Version proto error") 34 | } 35 | 36 | major, err := strconv.ParseInt(arr[1], 10, 64) 37 | if err != nil || major < 0 { 38 | t.Fatalf("Version major error") 39 | } 40 | 41 | minor, err := strconv.ParseInt(arr[2], 10, 64) 42 | if err != nil || minor < 0 { 43 | t.Fatalf("Version minor error") 44 | } 45 | } 46 | 47 | func TestVersion(t *testing.T) { 48 | proto := Proto(Full()) 49 | major := Major(Full()) 50 | minor := Minor(Full()) 51 | parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor) 52 | version := Full() 53 | if parseVerion != version { 54 | t.Fatalf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | # compile for version 2 | make 3 | if [ $? -ne 0 ]; then 4 | echo "make error" 5 | exit 1 6 | fi 7 | 8 | frp_version=`./bin/frps --version` 9 | echo "build version: $frp_version" 10 | 11 | # cross_compiles 12 | make -f ./Makefile.cross-compiles 13 | 14 | rm -rf ./packages 15 | mkdir ./packages 16 | 17 | os_all='linux windows darwin' 18 | arch_all='386 amd64 arm mips64 mips64le' 19 | 20 | for os in $os_all; do 21 | for arch in $arch_all; do 22 | frp_dir_name="frp_${frp_version}_${os}_${arch}" 23 | frp_path="./packages/frp_${frp_version}_${os}_${arch}" 24 | 25 | if [ "x${os}" = x"windows" ]; then 26 | if [ ! -f "./frpc_${os}_${arch}.exe" ]; then 27 | continue 28 | fi 29 | if [ ! -f "./frps_${os}_${arch}.exe" ]; then 30 | continue 31 | fi 32 | mkdir ${frp_path} 33 | mv ./frpc_${os}_${arch}.exe ${frp_path}/frpc.exe 34 | mv ./frps_${os}_${arch}.exe ${frp_path}/frps.exe 35 | else 36 | if [ ! -f "./frpc_${os}_${arch}" ]; then 37 | continue 38 | fi 39 | if [ ! -f "./frps_${os}_${arch}" ]; then 40 | continue 41 | fi 42 | mkdir ${frp_path} 43 | mv ./frpc_${os}_${arch} ${frp_path}/frpc 44 | mv ./frps_${os}_${arch} ${frp_path}/frps 45 | fi 46 | cp ./LICENSE ${frp_path} 47 | cp ./conf/* ${frp_path} 48 | 49 | # packages 50 | cd ./packages 51 | if [ "x${os}" = x"windows" ]; then 52 | zip -rq ${frp_dir_name}.zip ${frp_dir_name} 53 | else 54 | tar -zcf ${frp_dir_name}.tar.gz ${frp_dir_name} 55 | fi 56 | cd .. 57 | rm -rf ${frp_path} 58 | done 59 | done 60 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/broadcast/broadcast_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package broadcast 16 | 17 | import ( 18 | "sync" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | var ( 24 | totalNum int = 5 25 | succNum int = 0 26 | mutex sync.Mutex 27 | ) 28 | 29 | func TestBroadcast(t *testing.T) { 30 | b := NewBroadcast() 31 | if b == nil { 32 | t.Fatalf("New Broadcast error, nil return") 33 | } 34 | defer b.Close() 35 | 36 | var wait sync.WaitGroup 37 | wait.Add(totalNum) 38 | for i := 0; i < totalNum; i++ { 39 | go worker(b, &wait) 40 | } 41 | 42 | time.Sleep(1e6 * 20) 43 | msg := "test" 44 | b.In() <- msg 45 | 46 | wait.Wait() 47 | if succNum != totalNum { 48 | t.Fatalf("TotalNum %d, FailNum(timeout) %d", totalNum, totalNum-succNum) 49 | } 50 | } 51 | 52 | func worker(b *Broadcast, wait *sync.WaitGroup) { 53 | defer wait.Done() 54 | msgChan := b.Reg() 55 | 56 | // exit if nothing got in 2 seconds 57 | timeout := make(chan bool, 1) 58 | go func() { 59 | time.Sleep(time.Duration(2) * time.Second) 60 | timeout <- true 61 | }() 62 | 63 | select { 64 | case item := <-msgChan: 65 | msg := item.(string) 66 | if msg == "test" { 67 | mutex.Lock() 68 | succNum++ 69 | mutex.Unlock() 70 | } else { 71 | break 72 | } 73 | 74 | case <-timeout: 75 | break 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/models/msg/msg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package msg 16 | 17 | type GeneralRes struct { 18 | Code int64 `json:"code"` 19 | Msg string `json:"msg"` 20 | } 21 | 22 | // messages between control connections of frpc and frps 23 | type ControlReq struct { 24 | Type int64 `json:"type"` 25 | ProxyName string `json:"proxy_name"` 26 | AuthKey string `json:"auth_key"` 27 | UseEncryption bool `json:"use_encryption"` 28 | UseGzip bool `json:"use_gzip"` 29 | PoolCount int64 `json:"pool_count"` 30 | 31 | // configures used if privilege_mode is enabled 32 | PrivilegeMode bool `json:"privilege_mode"` 33 | PrivilegeKey string `json:"privilege_key"` 34 | ProxyType string `json:"proxy_type"` 35 | RemotePort int64 `json:"remote_port"` 36 | CustomDomains []string `json:"custom_domains, omitempty"` 37 | Locations []string `json:"locations"` 38 | HostHeaderRewrite string `json:"host_header_rewrite"` 39 | HttpUserName string `json:"http_username"` 40 | HttpPassWord string `json:"http_password"` 41 | SubDomain string `json:"subdomain"` 42 | Timestamp int64 `json:"timestamp"` 43 | } 44 | 45 | type ControlRes struct { 46 | Type int64 `json:"type"` 47 | Code int64 `json:"code"` 48 | Msg string `json:"msg"` 49 | } 50 | -------------------------------------------------------------------------------- /src/assets/static/font/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Thu Aug 4 18:45:29 2016 6 | By admin 7 | 8 | 9 | 10 | 24 | 26 | 28 | 30 | 32 | 36 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/models/server/dashboard_api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "net/http" 21 | 22 | "github.com/fatedier/frp/src/models/metric" 23 | "github.com/fatedier/frp/src/utils/log" 24 | ) 25 | 26 | type GeneralResponse struct { 27 | Code int64 `json:"code"` 28 | Msg string `json:"msg"` 29 | } 30 | 31 | func apiReload(w http.ResponseWriter, r *http.Request) { 32 | var buf []byte 33 | res := &GeneralResponse{} 34 | defer func() { 35 | log.Info("Http response [/api/reload]: %s", string(buf)) 36 | }() 37 | 38 | log.Info("Http request: [/api/reload]") 39 | err := ReloadConf(ConfigFile) 40 | if err != nil { 41 | res.Code = 2 42 | res.Msg = fmt.Sprintf("%v", err) 43 | log.Error("frps reload error: %v", err) 44 | } 45 | 46 | buf, _ = json.Marshal(res) 47 | w.Write(buf) 48 | } 49 | 50 | type ProxiesResponse struct { 51 | Code int64 `json:"code"` 52 | Msg string `json:"msg"` 53 | Proxies []*metric.ServerMetric `json:"proxies"` 54 | } 55 | 56 | func apiProxies(w http.ResponseWriter, r *http.Request) { 57 | var buf []byte 58 | res := &ProxiesResponse{} 59 | defer func() { 60 | log.Info("Http response [/api/proxies]: code [%d]", res.Code) 61 | }() 62 | 63 | log.Info("Http request: [/api/proxies]") 64 | res.Proxies = metric.GetAllProxyMetrics() 65 | buf, _ = json.Marshal(res) 66 | w.Write(buf) 67 | } 68 | -------------------------------------------------------------------------------- /src/models/msg/udp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package msg 16 | 17 | import ( 18 | "encoding/base64" 19 | "encoding/json" 20 | "net" 21 | ) 22 | 23 | type UdpPacket struct { 24 | Content []byte `json:"-"` 25 | Src *net.UDPAddr `json:"-"` 26 | Dst *net.UDPAddr `json:"-"` 27 | 28 | EncodeContent string `json:"content"` 29 | SrcStr string `json:"src"` 30 | DstStr string `json:"dst"` 31 | } 32 | 33 | func NewUdpPacket(content []byte, src, dst *net.UDPAddr) *UdpPacket { 34 | up := &UdpPacket{ 35 | Src: src, 36 | Dst: dst, 37 | EncodeContent: base64.StdEncoding.EncodeToString(content), 38 | SrcStr: src.String(), 39 | DstStr: dst.String(), 40 | } 41 | return up 42 | } 43 | 44 | // parse one udp packet struct to bytes 45 | func (up *UdpPacket) Pack() []byte { 46 | b, _ := json.Marshal(up) 47 | return b 48 | } 49 | 50 | // parse from bytes to UdpPacket struct 51 | func (up *UdpPacket) UnPack(packet []byte) error { 52 | err := json.Unmarshal(packet, &up) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | up.Content, err = base64.StdEncoding.DecodeString(up.EncodeContent) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | up.Src, err = net.ResolveUDPAddr("udp", up.SrcStr) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | up.Dst, err = net.ResolveUDPAddr("udp", up.DstStr) 68 | if err != nil { 69 | return err 70 | } 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /src/assets/assets.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package assets 16 | 17 | //go:generate statik -src=./static 18 | //go:generate go fmt statik/statik.go 19 | 20 | import ( 21 | "io/ioutil" 22 | "net/http" 23 | "os" 24 | "path" 25 | 26 | "github.com/rakyll/statik/fs" 27 | 28 | _ "github.com/fatedier/frp/src/assets/statik" 29 | ) 30 | 31 | var ( 32 | // store static files in memory by statik 33 | FileSystem http.FileSystem 34 | 35 | // if prefix is not empty, we get file content from disk 36 | prefixPath string 37 | ) 38 | 39 | // if path is empty, load assets in memory 40 | // or set FileSystem using disk files 41 | func Load(path string) (err error) { 42 | prefixPath = path 43 | if prefixPath != "" { 44 | FileSystem = http.Dir(prefixPath) 45 | return nil 46 | } else { 47 | FileSystem, err = fs.New() 48 | } 49 | return err 50 | } 51 | 52 | func ReadFile(file string) (content string, err error) { 53 | if prefixPath == "" { 54 | file, err := FileSystem.Open(path.Join("/", file)) 55 | if err != nil { 56 | return content, err 57 | } 58 | buf, err := ioutil.ReadAll(file) 59 | if err != nil { 60 | return content, err 61 | } 62 | content = string(buf) 63 | } else { 64 | file, err := os.Open(path.Join(prefixPath, file)) 65 | if err != nil { 66 | return content, err 67 | } 68 | buf, err := ioutil.ReadAll(file) 69 | if err != nil { 70 | return content, err 71 | } 72 | content = string(buf) 73 | } 74 | return content, err 75 | } 76 | -------------------------------------------------------------------------------- /src/utils/pcrypto/pcrypto_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pcrypto 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | var ( 22 | pp *Pcrypto 23 | ) 24 | 25 | func init() { 26 | pp = &Pcrypto{} 27 | pp.Init([]byte("12234567890123451223456789012345321:wq")) 28 | } 29 | 30 | func TestEncrypt(t *testing.T) { 31 | testStr := "Test Encrypt!" 32 | res, err := pp.Encrypt([]byte(testStr)) 33 | if err != nil { 34 | t.Fatalf("encrypt error: %v", err) 35 | } 36 | 37 | res, err = pp.Decrypt([]byte(res)) 38 | if err != nil { 39 | t.Fatalf("decrypt error: %v", err) 40 | } 41 | 42 | if string(res) != testStr { 43 | t.Fatalf("test encrypt error, from [%s] to [%s]", testStr, string(res)) 44 | } 45 | } 46 | 47 | func TestCompression(t *testing.T) { 48 | testStr := "Test Compression!" 49 | res, err := pp.Compression([]byte(testStr)) 50 | if err != nil { 51 | t.Fatalf("compression error: %v", err) 52 | } 53 | 54 | res, err = pp.Decompression(res) 55 | if err != nil { 56 | t.Fatalf("decompression error: %v", err) 57 | } 58 | 59 | if string(res) != testStr { 60 | t.Fatalf("test compression error, from [%s] to [%s]", testStr, string(res)) 61 | } 62 | } 63 | 64 | func BenchmarkEncrypt(b *testing.B) { 65 | testStr := "Test Encrypt!" 66 | for i := 0; i < b.N; i++ { 67 | pp.Encrypt([]byte(testStr)) 68 | } 69 | } 70 | 71 | func BenchmarkDecrypt(b *testing.B) { 72 | testStr := "Test Encrypt!" 73 | res, _ := pp.Encrypt([]byte(testStr)) 74 | for i := 0; i < b.N; i++ { 75 | pp.Decrypt([]byte(res)) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/utils/log/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package log 16 | 17 | import ( 18 | "fmt" 19 | "github.com/astaxie/beego/logs" 20 | ) 21 | 22 | var Log *logs.BeeLogger 23 | 24 | func init() { 25 | Log = logs.NewLogger(1000) 26 | Log.EnableFuncCallDepth(true) 27 | Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1) 28 | } 29 | 30 | func InitLog(logWay string, logFile string, logLevel string, maxdays int64) { 31 | SetLogFile(logWay, logFile, maxdays) 32 | SetLogLevel(logLevel) 33 | } 34 | 35 | // logWay: file or console 36 | func SetLogFile(logWay string, logFile string, maxdays int64) { 37 | if logWay == "console" { 38 | Log.SetLogger("console", "") 39 | } else { 40 | params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays) 41 | Log.SetLogger("file", params) 42 | } 43 | } 44 | 45 | // value: error, warning, info, debug 46 | func SetLogLevel(logLevel string) { 47 | level := 4 // warning 48 | switch logLevel { 49 | case "error": 50 | level = 3 51 | case "warn": 52 | level = 4 53 | case "info": 54 | level = 6 55 | case "debug": 56 | level = 7 57 | default: 58 | level = 4 59 | } 60 | Log.SetLevel(level) 61 | } 62 | 63 | // wrap log 64 | func Error(format string, v ...interface{}) { 65 | Log.Error(format, v...) 66 | } 67 | 68 | func Warn(format string, v ...interface{}) { 69 | Log.Warn(format, v...) 70 | } 71 | 72 | func Info(format string, v ...interface{}) { 73 | Log.Info(format, v...) 74 | } 75 | 76 | func Debug(format string, v ...interface{}) { 77 | Log.Debug(format, v...) 78 | } 79 | -------------------------------------------------------------------------------- /src/utils/broadcast/broadcast.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package broadcast 16 | 17 | type Broadcast struct { 18 | listeners []chan interface{} 19 | reg chan (chan interface{}) 20 | unreg chan (chan interface{}) 21 | in chan interface{} 22 | stop chan int64 23 | stopStatus bool 24 | } 25 | 26 | func NewBroadcast() *Broadcast { 27 | b := &Broadcast{ 28 | listeners: make([]chan interface{}, 0), 29 | reg: make(chan (chan interface{})), 30 | unreg: make(chan (chan interface{})), 31 | in: make(chan interface{}), 32 | stop: make(chan int64), 33 | stopStatus: false, 34 | } 35 | 36 | go func() { 37 | for { 38 | select { 39 | case l := <-b.unreg: 40 | // remove L from b.listeners 41 | // this operation is slow: O(n) but not used frequently 42 | // unlike iterating over listeners 43 | oldListeners := b.listeners 44 | b.listeners = make([]chan interface{}, 0, len(oldListeners)) 45 | for _, oldL := range oldListeners { 46 | if l != oldL { 47 | b.listeners = append(b.listeners, oldL) 48 | } 49 | } 50 | 51 | case l := <-b.reg: 52 | b.listeners = append(b.listeners, l) 53 | 54 | case item := <-b.in: 55 | for _, l := range b.listeners { 56 | l <- item 57 | } 58 | 59 | case _ = <-b.stop: 60 | b.stopStatus = true 61 | break 62 | } 63 | } 64 | }() 65 | 66 | return b 67 | } 68 | 69 | func (b *Broadcast) In() chan interface{} { 70 | return b.in 71 | } 72 | 73 | func (b *Broadcast) Reg() chan interface{} { 74 | listener := make(chan interface{}) 75 | b.reg <- listener 76 | return listener 77 | } 78 | 79 | func (b *Broadcast) UnReg(listener chan interface{}) { 80 | b.unreg <- listener 81 | } 82 | 83 | func (b *Broadcast) Close() { 84 | if b.stopStatus == false { 85 | b.stop <- 1 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/utils/vhost/router.go: -------------------------------------------------------------------------------- 1 | package vhost 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | "sync" 7 | ) 8 | 9 | type VhostRouters struct { 10 | RouterByDomain map[string][]*VhostRouter 11 | mutex sync.RWMutex 12 | } 13 | 14 | type VhostRouter struct { 15 | domain string 16 | location string 17 | listener *Listener 18 | } 19 | 20 | func NewVhostRouters() *VhostRouters { 21 | return &VhostRouters{ 22 | RouterByDomain: make(map[string][]*VhostRouter), 23 | } 24 | } 25 | 26 | func (r *VhostRouters) Add(domain, location string, l *Listener) { 27 | r.mutex.Lock() 28 | defer r.mutex.Unlock() 29 | 30 | vrs, found := r.RouterByDomain[domain] 31 | if !found { 32 | vrs = make([]*VhostRouter, 0, 1) 33 | } 34 | 35 | vr := &VhostRouter{ 36 | domain: domain, 37 | location: location, 38 | listener: l, 39 | } 40 | vrs = append(vrs, vr) 41 | 42 | sort.Sort(sort.Reverse(ByLocation(vrs))) 43 | r.RouterByDomain[domain] = vrs 44 | } 45 | 46 | func (r *VhostRouters) Del(domain, location string) { 47 | r.mutex.Lock() 48 | defer r.mutex.Unlock() 49 | 50 | vrs, found := r.RouterByDomain[domain] 51 | if !found { 52 | return 53 | } 54 | 55 | for i, vr := range vrs { 56 | if vr.location == location { 57 | if len(vrs) > i+1 { 58 | r.RouterByDomain[domain] = append(vrs[:i], vrs[i+1:]...) 59 | } else { 60 | r.RouterByDomain[domain] = vrs[:i] 61 | } 62 | } 63 | } 64 | } 65 | 66 | func (r *VhostRouters) Get(host, path string) (vr *VhostRouter, exist bool) { 67 | r.mutex.RLock() 68 | defer r.mutex.RUnlock() 69 | 70 | vrs, found := r.RouterByDomain[host] 71 | if !found { 72 | return 73 | } 74 | 75 | // can't support load balance, will to do 76 | for _, vr = range vrs { 77 | if strings.HasPrefix(path, vr.location) { 78 | return vr, true 79 | } 80 | } 81 | 82 | return 83 | } 84 | 85 | func (r *VhostRouters) Exist(host, path string) (vr *VhostRouter, exist bool) { 86 | r.mutex.RLock() 87 | defer r.mutex.RUnlock() 88 | 89 | vrs, found := r.RouterByDomain[host] 90 | if !found { 91 | return 92 | } 93 | 94 | for _, vr = range vrs { 95 | if path == vr.location { 96 | return vr, true 97 | } 98 | } 99 | 100 | return 101 | } 102 | 103 | // sort by location 104 | type ByLocation []*VhostRouter 105 | 106 | func (a ByLocation) Len() int { 107 | return len(a) 108 | } 109 | func (a ByLocation) Swap(i, j int) { 110 | a[i], a[j] = a[j], a[i] 111 | } 112 | func (a ByLocation) Less(i, j int) bool { 113 | return strings.Compare(a[i].location, a[j].location) < 0 114 | } 115 | -------------------------------------------------------------------------------- /conf/frps.ini: -------------------------------------------------------------------------------- 1 | # [common] is integral section 2 | [common] 3 | # A literal address or host name for IPv6 must be enclosed 4 | # in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80" 5 | bind_addr = 0.0.0.0 6 | bind_port = 7000 7 | 8 | # if you want to support virtual host, you must set the http port for listening (optional) 9 | vhost_http_port = 80 10 | vhost_https_port = 443 11 | 12 | # if you want to configure or reload frps by dashboard, dashboard_port must be set 13 | dashboard_port = 7500 14 | 15 | # dashboard user and pwd for basic auth protect, if not set, both default value is admin 16 | dashboard_user = admin 17 | dashboard_pwd = admin 18 | 19 | # dashboard assets directory(only for debug mode) 20 | # assets_dir = ./static 21 | # console or real logFile path like ./frps.log 22 | log_file = ./frps.log 23 | 24 | # debug, info, warn, error 25 | log_level = info 26 | 27 | log_max_days = 3 28 | 29 | # if you enable privilege mode, frpc can create a proxy without pre-configure in frps when privilege_token is correct 30 | privilege_mode = true 31 | privilege_token = 12345678 32 | 33 | # heartbeat configure, it's not recommended to modify the default value 34 | # the default value of heartbeat_timeout is 30 35 | # heartbeat_timeout = 30 36 | 37 | # only allow frpc to bind ports you list, if you set nothing, there won't be any limit 38 | privilege_allow_ports = 2000-3000,3001,3003,4000-50000 39 | 40 | # pool_count in each proxy will change to max_pool_count if they exceed the maximum value 41 | max_pool_count = 100 42 | 43 | # authentication_timeout means the timeout interval (seconds) when the frpc connects frps 44 | # if authentication_timeout is zero, the time is not verified, default is 900s 45 | authentication_timeout = 900 46 | 47 | # if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file 48 | # when subdomain is test, the host used by routing is test.frps.com 49 | subdomain_host = frps.com 50 | 51 | # ssh is the proxy name, client will use this name and auth_token to connect to server 52 | [ssh] 53 | type = tcp 54 | auth_token = 123 55 | bind_addr = 0.0.0.0 56 | listen_port = 6000 57 | 58 | [dns] 59 | type = udp 60 | auth_token = 123 61 | bind_addr = 0.0.0.0 62 | listen_port = 5353 63 | 64 | [web01] 65 | # if type equals http, vhost_http_port must be set 66 | type = http 67 | auth_token = 123 68 | # if proxy type equals http, custom_domains must be set separated by commas 69 | custom_domains = web01.yourdomain.com,web01.yourdomain2.com 70 | 71 | [web02] 72 | # if type equals https, vhost_https_port must be set 73 | type = https 74 | auth_token = 123 75 | custom_domains = web02.yourdomain.com 76 | -------------------------------------------------------------------------------- /conf/frpc.ini: -------------------------------------------------------------------------------- 1 | # [common] is integral section 2 | [common] 3 | # A literal address or host name for IPv6 must be enclosed 4 | # in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80" 5 | server_addr = 0.0.0.0 6 | server_port = 7000 7 | 8 | # if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables 9 | # http_proxy = http://user:pwd@192.168.1.128:8080 10 | 11 | # console or real logFile path like ./frpc.log 12 | log_file = ./frpc.log 13 | 14 | # debug, info, warn, error 15 | log_level = info 16 | 17 | log_max_days = 3 18 | 19 | # for authentication 20 | auth_token = 123 21 | 22 | # for privilege mode 23 | privilege_token = 12345678 24 | 25 | # heartbeat configure, it's not recommended to modify the default value 26 | # the default value of heartbeat_interval is 10 and heartbeat_timeout is 30 27 | # heartbeat_interval = 10 28 | # heartbeat_timeout = 30 29 | 30 | # ssh is the proxy name same as server's configuration 31 | [ssh] 32 | # tcp | http, default is tcp 33 | type = tcp 34 | local_ip = 127.0.0.1 35 | local_port = 22 36 | # true or false, if true, messages between frps and frpc will be encrypted, default is false 37 | use_encryption = false 38 | # default is false 39 | use_gzip = false 40 | 41 | [dns] 42 | type = udp 43 | local_ip = 114.114.114.114 44 | local_port = 53 45 | 46 | # 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 47 | [web01] 48 | type = http 49 | local_ip = 127.0.0.1 50 | local_port = 80 51 | use_gzip = true 52 | # connections will be established in advance, default value is zero 53 | pool_count = 20 54 | # http username and password are safety certification for http protocol 55 | # if not set, you can access this custom_domains without certification 56 | http_user = admin 57 | http_pwd = admin 58 | # if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com 59 | subdomain = test 60 | 61 | [web02] 62 | type = http 63 | local_ip = 127.0.0.1 64 | local_port = 8000 65 | 66 | [privilege_ssh] 67 | # if privilege_mode is enabled, this proxy will be created automatically 68 | privilege_mode = true 69 | type = tcp 70 | local_ip = 127.0.0.1 71 | local_port = 22 72 | use_encryption = true 73 | use_gzip = false 74 | remote_port = 6001 75 | 76 | [privilege_web] 77 | privilege_mode = true 78 | type = http 79 | local_ip = 127.0.0.1 80 | local_port = 80 81 | use_gzip = true 82 | custom_domains = web03.yourdomain.com 83 | # locations is only useful for http type 84 | locations = /,/pic 85 | host_header_rewrite = example.com 86 | subdomain = dev 87 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt-go/README.md: -------------------------------------------------------------------------------- 1 | docopt-go 2 | ========= 3 | 4 | [![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go) 5 | [![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go) 6 | [![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go) 7 | 8 | An implementation of [docopt](http://docopt.org/) in the 9 | [Go](http://golang.org/) programming language. 10 | 11 | **docopt** helps you create *beautiful* command-line interfaces easily: 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "github.com/docopt/docopt-go" 19 | ) 20 | 21 | func main() { 22 | usage := `Naval Fate. 23 | 24 | Usage: 25 | naval_fate ship new ... 26 | naval_fate ship move [--speed=] 27 | naval_fate ship shoot 28 | naval_fate mine (set|remove) [--moored|--drifting] 29 | naval_fate -h | --help 30 | naval_fate --version 31 | 32 | Options: 33 | -h --help Show this screen. 34 | --version Show version. 35 | --speed= Speed in knots [default: 10]. 36 | --moored Moored (anchored) mine. 37 | --drifting Drifting mine.` 38 | 39 | arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false) 40 | fmt.Println(arguments) 41 | } 42 | ``` 43 | 44 | **docopt** parses command-line arguments based on a help message. Don't 45 | write parser code: a good help message already has all the necessary 46 | information in it. 47 | 48 | ## Installation 49 | 50 | ⚠ Use the alias “docopt-go”. To use docopt in your Go code: 51 | 52 | ```go 53 | import "github.com/docopt/docopt-go" 54 | ``` 55 | 56 | To install docopt according to your `$GOPATH`: 57 | 58 | ```console 59 | $ go get github.com/docopt/docopt-go 60 | ``` 61 | 62 | ## API 63 | 64 | ```go 65 | func Parse(doc string, argv []string, help bool, version string, 66 | optionsFirst bool, exit ...bool) (map[string]interface{}, error) 67 | ``` 68 | Parse `argv` based on the command-line interface described in `doc`. 69 | 70 | Given a conventional command-line help message, docopt creates a parser and 71 | processes the arguments. See 72 | https://github.com/docopt/docopt#help-message-format for a description of the 73 | help message format. If `argv` is `nil`, `os.Args[1:]` is used. 74 | 75 | docopt returns a map of option names to the values parsed from `argv`, and an 76 | error or `nil`. 77 | 78 | More documentation for docopt is available at 79 | [GoDoc.org](https://godoc.org/github.com/docopt/docopt.go). 80 | 81 | ## Testing 82 | 83 | All tests from the Python version are implemented and passing 84 | at [Travis CI](https://travis-ci.org/docopt/docopt.go). New 85 | language-agnostic tests have been added 86 | to [test_golang.docopt](test_golang.docopt). 87 | 88 | To run tests for docopt-go, use `go test`. 89 | -------------------------------------------------------------------------------- /src/models/server/dashboard.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "encoding/base64" 19 | "fmt" 20 | "net" 21 | "net/http" 22 | "strings" 23 | "time" 24 | 25 | "github.com/fatedier/frp/src/assets" 26 | ) 27 | 28 | var ( 29 | httpServerReadTimeout = 10 * time.Second 30 | httpServerWriteTimeout = 10 * time.Second 31 | ) 32 | 33 | func RunDashboardServer(addr string, port int64) (err error) { 34 | // url router 35 | mux := http.NewServeMux() 36 | // api, see dashboard_api.go 37 | mux.HandleFunc("/api/reload", use(apiReload, basicAuth)) 38 | mux.HandleFunc("/api/proxies", apiProxies) 39 | 40 | // view, see dashboard_view.go 41 | mux.Handle("/favicon.ico", http.FileServer(assets.FileSystem)) 42 | mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(assets.FileSystem))) 43 | mux.HandleFunc("/", use(viewDashboard, basicAuth)) 44 | 45 | address := fmt.Sprintf("%s:%d", addr, port) 46 | server := &http.Server{ 47 | Addr: address, 48 | Handler: mux, 49 | ReadTimeout: httpServerReadTimeout, 50 | WriteTimeout: httpServerWriteTimeout, 51 | } 52 | if address == "" { 53 | address = ":http" 54 | } 55 | ln, err := net.Listen("tcp", address) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | go server.Serve(ln) 61 | return 62 | } 63 | 64 | func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc { 65 | for _, m := range middleware { 66 | h = m(h) 67 | } 68 | 69 | return h 70 | } 71 | 72 | func basicAuth(h http.HandlerFunc) http.HandlerFunc { 73 | return func(w http.ResponseWriter, r *http.Request) { 74 | 75 | w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) 76 | 77 | s := strings.SplitN(r.Header.Get("Authorization"), " ", 2) 78 | if len(s) != 2 { 79 | http.Error(w, "Not authorized", 401) 80 | return 81 | } 82 | 83 | b, err := base64.StdEncoding.DecodeString(s[1]) 84 | if err != nil { 85 | http.Error(w, err.Error(), 401) 86 | return 87 | } 88 | 89 | pair := strings.SplitN(string(b), ":", 2) 90 | if len(pair) != 2 { 91 | http.Error(w, "Not authorized", 401) 92 | return 93 | } 94 | 95 | if pair[0] != DashboardUsername || pair[1] != DashboardPassword { 96 | http.Error(w, "Not authorized", 401) 97 | return 98 | } 99 | 100 | h.ServeHTTP(w, r) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/console.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logs 16 | 17 | import ( 18 | "encoding/json" 19 | "os" 20 | "runtime" 21 | "time" 22 | ) 23 | 24 | // brush is a color join function 25 | type brush func(string) string 26 | 27 | // newBrush return a fix color Brush 28 | func newBrush(color string) brush { 29 | pre := "\033[" 30 | reset := "\033[0m" 31 | return func(text string) string { 32 | return pre + color + "m" + text + reset 33 | } 34 | } 35 | 36 | var colors = []brush{ 37 | newBrush("1;37"), // Emergency white 38 | newBrush("1;36"), // Alert cyan 39 | newBrush("1;35"), // Critical magenta 40 | newBrush("1;31"), // Error red 41 | newBrush("1;33"), // Warning yellow 42 | newBrush("1;32"), // Notice green 43 | newBrush("1;34"), // Informational blue 44 | newBrush("1;34"), // Debug blue 45 | } 46 | 47 | // consoleWriter implements LoggerInterface and writes messages to terminal. 48 | type consoleWriter struct { 49 | lg *logWriter 50 | Level int `json:"level"` 51 | Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color 52 | } 53 | 54 | // NewConsole create ConsoleWriter returning as LoggerInterface. 55 | func NewConsole() Logger { 56 | cw := &consoleWriter{ 57 | lg: newLogWriter(os.Stdout), 58 | Level: LevelDebug, 59 | Colorful: runtime.GOOS != "windows", 60 | } 61 | return cw 62 | } 63 | 64 | // Init init console logger. 65 | // jsonConfig like '{"level":LevelTrace}'. 66 | func (c *consoleWriter) Init(jsonConfig string) error { 67 | if len(jsonConfig) == 0 { 68 | return nil 69 | } 70 | err := json.Unmarshal([]byte(jsonConfig), c) 71 | if runtime.GOOS == "windows" { 72 | c.Colorful = false 73 | } 74 | return err 75 | } 76 | 77 | // WriteMsg write message in console. 78 | func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { 79 | if level > c.Level { 80 | return nil 81 | } 82 | if c.Colorful { 83 | msg = colors[level](msg) 84 | } 85 | c.lg.println(when, msg) 86 | return nil 87 | } 88 | 89 | // Destroy implementing method. empty. 90 | func (c *consoleWriter) Destroy() { 91 | 92 | } 93 | 94 | // Flush implementing method. empty. 95 | func (c *consoleWriter) Flush() { 96 | 97 | } 98 | 99 | func init() { 100 | Register(AdapterConsole, NewConsole) 101 | } 102 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logs 16 | 17 | import ( 18 | "encoding/json" 19 | "io" 20 | "net" 21 | "time" 22 | ) 23 | 24 | // connWriter implements LoggerInterface. 25 | // it writes messages in keep-live tcp connection. 26 | type connWriter struct { 27 | lg *logWriter 28 | innerWriter io.WriteCloser 29 | ReconnectOnMsg bool `json:"reconnectOnMsg"` 30 | Reconnect bool `json:"reconnect"` 31 | Net string `json:"net"` 32 | Addr string `json:"addr"` 33 | Level int `json:"level"` 34 | } 35 | 36 | // NewConn create new ConnWrite returning as LoggerInterface. 37 | func NewConn() Logger { 38 | conn := new(connWriter) 39 | conn.Level = LevelTrace 40 | return conn 41 | } 42 | 43 | // Init init connection writer with json config. 44 | // json config only need key "level". 45 | func (c *connWriter) Init(jsonConfig string) error { 46 | return json.Unmarshal([]byte(jsonConfig), c) 47 | } 48 | 49 | // WriteMsg write message in connection. 50 | // if connection is down, try to re-connect. 51 | func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { 52 | if level > c.Level { 53 | return nil 54 | } 55 | if c.needToConnectOnMsg() { 56 | err := c.connect() 57 | if err != nil { 58 | return err 59 | } 60 | } 61 | 62 | if c.ReconnectOnMsg { 63 | defer c.innerWriter.Close() 64 | } 65 | 66 | c.lg.println(when, msg) 67 | return nil 68 | } 69 | 70 | // Flush implementing method. empty. 71 | func (c *connWriter) Flush() { 72 | 73 | } 74 | 75 | // Destroy destroy connection writer and close tcp listener. 76 | func (c *connWriter) Destroy() { 77 | if c.innerWriter != nil { 78 | c.innerWriter.Close() 79 | } 80 | } 81 | 82 | func (c *connWriter) connect() error { 83 | if c.innerWriter != nil { 84 | c.innerWriter.Close() 85 | c.innerWriter = nil 86 | } 87 | 88 | conn, err := net.Dial(c.Net, c.Addr) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | if tcpConn, ok := conn.(*net.TCPConn); ok { 94 | tcpConn.SetKeepAlive(true) 95 | } 96 | 97 | c.innerWriter = conn 98 | c.lg = newLogWriter(conn) 99 | return nil 100 | } 101 | 102 | func (c *connWriter) needToConnectOnMsg() bool { 103 | if c.Reconnect { 104 | c.Reconnect = false 105 | return true 106 | } 107 | 108 | if c.innerWriter == nil { 109 | return true 110 | } 111 | 112 | return c.ReconnectOnMsg 113 | } 114 | 115 | func init() { 116 | Register(AdapterConn, NewConn) 117 | } 118 | -------------------------------------------------------------------------------- /src/cmd/frpc/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "strconv" 21 | "strings" 22 | "sync" 23 | 24 | docopt "github.com/docopt/docopt-go" 25 | 26 | "github.com/fatedier/frp/src/models/client" 27 | "github.com/fatedier/frp/src/utils/log" 28 | "github.com/fatedier/frp/src/utils/version" 29 | ) 30 | 31 | var ( 32 | configFile string = "./frpc.ini" 33 | ) 34 | 35 | var usage string = `frpc is the client of frp 36 | 37 | Usage: 38 | frpc [-c config_file] [-L log_file] [--log-level=] [--server-addr=] 39 | frpc -h | --help 40 | frpc -v | --version 41 | 42 | Options: 43 | -c config_file set config file 44 | -L log_file set output log file, including console 45 | --log-level= set log level: debug, info, warn, error 46 | --server-addr= addr which frps is listening for, example: 0.0.0.0:7000 47 | -h --help show this screen 48 | --version show version 49 | ` 50 | 51 | func main() { 52 | // the configures parsed from file will be replaced by those from command line if exist 53 | args, err := docopt.Parse(usage, nil, true, version.Full(), false) 54 | 55 | if args["-c"] != nil { 56 | configFile = args["-c"].(string) 57 | } 58 | err = client.LoadConf(configFile) 59 | if err != nil { 60 | fmt.Println(err) 61 | os.Exit(-1) 62 | } 63 | 64 | if args["-L"] != nil { 65 | if args["-L"].(string) == "console" { 66 | client.LogWay = "console" 67 | } else { 68 | client.LogWay = "file" 69 | client.LogFile = args["-L"].(string) 70 | } 71 | } 72 | 73 | if args["--log-level"] != nil { 74 | client.LogLevel = args["--log-level"].(string) 75 | } 76 | 77 | if args["--server-addr"] != nil { 78 | addr := strings.Split(args["--server-addr"].(string), ":") 79 | if len(addr) != 2 { 80 | fmt.Println("--server-addr format error: example 0.0.0.0:7000") 81 | os.Exit(1) 82 | } 83 | serverPort, err := strconv.ParseInt(addr[1], 10, 64) 84 | if err != nil { 85 | fmt.Println("--server-addr format error, example 0.0.0.0:7000") 86 | os.Exit(1) 87 | } 88 | client.ServerAddr = addr[0] 89 | client.ServerPort = serverPort 90 | } 91 | 92 | if args["-v"] != nil { 93 | if args["-v"].(bool) { 94 | fmt.Println(version.Full()) 95 | os.Exit(0) 96 | } 97 | } 98 | 99 | log.InitLog(client.LogWay, client.LogFile, client.LogLevel, client.LogMaxDays) 100 | 101 | // wait until all control goroutine exit 102 | var wait sync.WaitGroup 103 | wait.Add(len(client.ProxyClients)) 104 | 105 | for _, client := range client.ProxyClients { 106 | go ControlProcess(client, &wait) 107 | } 108 | 109 | log.Info("Start frpc success") 110 | 111 | wait.Wait() 112 | log.Warn("All proxy exit!") 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/vaughan0/go-ini/ini.go: -------------------------------------------------------------------------------- 1 | // Package ini provides functions for parsing INI configuration files. 2 | package ini 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "io" 8 | "os" 9 | "regexp" 10 | "strings" 11 | ) 12 | 13 | var ( 14 | sectionRegex = regexp.MustCompile(`^\[(.*)\]$`) 15 | assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) 16 | ) 17 | 18 | // ErrSyntax is returned when there is a syntax error in an INI file. 19 | type ErrSyntax struct { 20 | Line int 21 | Source string // The contents of the erroneous line, without leading or trailing whitespace 22 | } 23 | 24 | func (e ErrSyntax) Error() string { 25 | return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source) 26 | } 27 | 28 | // A File represents a parsed INI file. 29 | type File map[string]Section 30 | 31 | // A Section represents a single section of an INI file. 32 | type Section map[string]string 33 | 34 | // Returns a named Section. A Section will be created if one does not already exist for the given name. 35 | func (f File) Section(name string) Section { 36 | section := f[name] 37 | if section == nil { 38 | section = make(Section) 39 | f[name] = section 40 | } 41 | return section 42 | } 43 | 44 | // Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup. 45 | func (f File) Get(section, key string) (value string, ok bool) { 46 | if s := f[section]; s != nil { 47 | value, ok = s[key] 48 | } 49 | return 50 | } 51 | 52 | // Loads INI data from a reader and stores the data in the File. 53 | func (f File) Load(in io.Reader) (err error) { 54 | bufin, ok := in.(*bufio.Reader) 55 | if !ok { 56 | bufin = bufio.NewReader(in) 57 | } 58 | return parseFile(bufin, f) 59 | } 60 | 61 | // Loads INI data from a named file and stores the data in the File. 62 | func (f File) LoadFile(file string) (err error) { 63 | in, err := os.Open(file) 64 | if err != nil { 65 | return 66 | } 67 | defer in.Close() 68 | return f.Load(in) 69 | } 70 | 71 | func parseFile(in *bufio.Reader, file File) (err error) { 72 | section := "" 73 | lineNum := 0 74 | for done := false; !done; { 75 | var line string 76 | if line, err = in.ReadString('\n'); err != nil { 77 | if err == io.EOF { 78 | done = true 79 | } else { 80 | return 81 | } 82 | } 83 | lineNum++ 84 | line = strings.TrimSpace(line) 85 | if len(line) == 0 { 86 | // Skip blank lines 87 | continue 88 | } 89 | if line[0] == ';' || line[0] == '#' { 90 | // Skip comments 91 | continue 92 | } 93 | 94 | if groups := assignRegex.FindStringSubmatch(line); groups != nil { 95 | key, val := groups[1], groups[2] 96 | key, val = strings.TrimSpace(key), strings.TrimSpace(val) 97 | file.Section(section)[key] = val 98 | } else if groups := sectionRegex.FindStringSubmatch(line); groups != nil { 99 | name := strings.TrimSpace(groups[1]) 100 | section = name 101 | // Create the section if it does not exist 102 | file.Section(section) 103 | } else { 104 | return ErrSyntax{lineNum, line} 105 | } 106 | 107 | } 108 | return nil 109 | } 110 | 111 | // Loads and returns a File from a reader. 112 | func Load(in io.Reader) (File, error) { 113 | file := make(File) 114 | err := file.Load(in) 115 | return file, err 116 | } 117 | 118 | // Loads and returns an INI File from a file on disk. 119 | func LoadFile(filename string) (File, error) { 120 | file := make(File) 121 | err := file.LoadFile(filename) 122 | return file, err 123 | } 124 | -------------------------------------------------------------------------------- /doc/quick_start_zh.md: -------------------------------------------------------------------------------- 1 | # frp 使用文档 2 | 3 | 相比于其他项目而言 frp 更易于部署和使用,这里我们用两个简单的示例来演示 frp 的使用过程。 4 | 5 | 1. 如何通过一台拥有公网IP地址的**服务器B**,访问处于公司内部网络环境中的**服务器A**的**ssh**端口,**服务器B**的IP地址为 x.x.x.x(测试时替换为真实的IP地址)。 6 | 2. 如何利用一台拥有公网IP地址的**服务器B**,使通过 **web01.yourdomain.com** 可以访问内网环境中**服务器A**上**8000端口**的web服务,**web02.yourdomain.com** 可以访问**服务器A**上**8001端口**的web服务。 7 | 8 | ### 下载源码 9 | 10 | 推荐直接使用 `go get github.com/fatedier/frp` 下载源代码安装,执行命令后代码将会拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 11 | 12 | 或者可以使用 `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp` 拷贝到相应目录下。 13 | 14 | 如果您想快速进行测试,也可以根据您服务器的操作系统及架构直接下载编译好的程序及示例配置文件,[https://github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases)。 15 | 16 | ### 编译 17 | 18 | 进入下载后的源码根目录,执行 `make` 命令,等待编译完成。 19 | 20 | 编译完成后, **bin** 目录下是编译好的可执行文件,**conf** 目录下是示例配置文件。 21 | 22 | ### 依赖 23 | 24 | * go 1.4 以上版本 25 | * godep (如果检查不存在,编译时会通过 `go get` 命令安装) 26 | 27 | ### 部署 28 | 29 | 1. 将 ./bin/frps 和 ./conf/frps.ini 拷贝至**服务器B**任意目录。 30 | 2. 将 ./bin/frpc 和 ./conf/frpc.ini 拷贝至**服务器A**任意目录。 31 | 3. 根据要实现的功能修改两边的配置文件,详细内容见后续章节说明。 32 | 4. 在服务器B执行 `nohup ./frps &` 或者 `nohup ./frps -c ./frps.ini &`。 33 | 5. 在服务器A执行 `nohup ./frpc &` 或者 `nohup ./frpc -c ./frpc.ini &`。 34 | 6. 通过 `ssh -oPort=6000 {user}@x.x.x.x` 测试是否能够成功连接**服务器A**({user}替换为**服务器A**上存在的真实用户),或通过浏览器访问自定义域名验证 http 服务是否转发成功。 35 | 36 | ## tcp 端口转发 37 | 38 | 转发 tcp 端口需要按照需求修改 frps 和 frpc 的配置文件。 39 | 40 | ### 配置文件 41 | 42 | #### frps.ini 43 | 44 | ```ini 45 | [common] 46 | bind_addr = 0.0.0.0 47 | # 用于接收 frpc 连接的端口 48 | bind_port = 7000 49 | log_file = ./frps.log 50 | log_level = info 51 | 52 | # ssh 为代理的自定义名称,可以有多个,不能重复,和frpc中名称对应 53 | [ssh] 54 | auth_token = 123 55 | bind_addr = 0.0.0.0 56 | # 最后将通过此端口访问后端服务 57 | listen_port = 6000 58 | ``` 59 | 60 | #### frpc.ini 61 | 62 | ```ini 63 | [common] 64 | # frps 所在服务器绑定的IP地址 65 | server_addr = x.x.x.x 66 | server_port = 7000 67 | log_file = ./frpc.log 68 | log_level = info 69 | # 用于身份验证 70 | auth_token = 123 71 | 72 | # ssh 需要和 frps.ini 中配置一致 73 | [ssh] 74 | # 需要转发的本地端口 75 | local_port = 22 76 | # 启用加密,frpc与frps之间通信加密,默认为 false 77 | use_encryption = true 78 | ``` 79 | 80 | ## http 端口转发,自定义域名绑定 81 | 82 | 如果只需要一对一的转发,例如**服务器B**的**80端口**转发**服务器A**的**8000端口**,则只需要配置 [tcp 端口转发](/doc/quick_start_zh.md#tcp-端口转发) 即可,如果需要使**服务器B**的**80端口**可以转发至**多个**web服务端口,则需要指定代理的类型为 http,并且在 frps 的配置文件中配置用于提供 http 转发服务的端口。 83 | 84 | 按照如下的内容修改配置文件后,需要将自定义域名的 **A 记录**解析到 [server_addr],如果 [server_addr] 是域名也可以将自定义域名的 **CNAME 记录**解析到 [server_addr]。 85 | 86 | 之后就可以通过自定义域名访问到本地的多个 web 服务。 87 | 88 | ### 配置文件 89 | 90 | #### frps.ini 91 | 92 | ```ini 93 | [common] 94 | bind_addr = 0.0.0.0 95 | bind_port = 7000 96 | # 如果需要支持http类型的代理则需要指定一个端口 97 | vhost_http_port = 80 98 | log_file = ./frps.log 99 | log_level = info 100 | 101 | [web01] 102 | # type 默认为 tcp,这里需要特别指定为 http 103 | type = http 104 | auth_token = 123 105 | # 自定义域名绑定,如果需要同时绑定多个以英文逗号分隔 106 | custom_domains = web01.yourdomain.com 107 | 108 | [web02] 109 | type = http 110 | auth_token = 123 111 | custom_domains = web02.yourdomain.com 112 | ``` 113 | 114 | #### frpc.ini 115 | 116 | ```ini 117 | [common] 118 | server_addr = x.x.x.x 119 | server_port = 7000 120 | log_file = ./frpc.log 121 | log_level = info 122 | auth_token = 123 123 | 124 | 125 | # 自定义域名在 frps.ini 中配置,方便做统一管理 126 | [web01] 127 | type = http 128 | local_ip = 127.0.0.1 129 | local_port = 8000 130 | # 可选是否加密 131 | use_encryption = true 132 | 133 | [web02] 134 | type = http 135 | local_ip = 127.0.0.1 136 | local_port = 8001 137 | ``` 138 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/multifile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logs 16 | 17 | import ( 18 | "encoding/json" 19 | "time" 20 | ) 21 | 22 | // A filesLogWriter manages several fileLogWriter 23 | // filesLogWriter will write logs to the file in json configuration and write the same level log to correspond file 24 | // means if the file name in configuration is project.log filesLogWriter will create project.error.log/project.debug.log 25 | // and write the error-level logs to project.error.log and write the debug-level logs to project.debug.log 26 | // the rotate attribute also acts like fileLogWriter 27 | type multiFileLogWriter struct { 28 | writers [LevelDebug + 1 + 1]*fileLogWriter // the last one for fullLogWriter 29 | fullLogWriter *fileLogWriter 30 | Separate []string `json:"separate"` 31 | } 32 | 33 | var levelNames = [...]string{"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"} 34 | 35 | // Init file logger with json config. 36 | // jsonConfig like: 37 | // { 38 | // "filename":"logs/beego.log", 39 | // "maxLines":0, 40 | // "maxsize":0, 41 | // "daily":true, 42 | // "maxDays":15, 43 | // "rotate":true, 44 | // "perm":0600, 45 | // "separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"], 46 | // } 47 | 48 | func (f *multiFileLogWriter) Init(config string) error { 49 | writer := newFileWriter().(*fileLogWriter) 50 | err := writer.Init(config) 51 | if err != nil { 52 | return err 53 | } 54 | f.fullLogWriter = writer 55 | f.writers[LevelDebug+1] = writer 56 | 57 | //unmarshal "separate" field to f.Separate 58 | json.Unmarshal([]byte(config), f) 59 | 60 | jsonMap := map[string]interface{}{} 61 | json.Unmarshal([]byte(config), &jsonMap) 62 | 63 | for i := LevelEmergency; i < LevelDebug+1; i++ { 64 | for _, v := range f.Separate { 65 | if v == levelNames[i] { 66 | jsonMap["filename"] = f.fullLogWriter.fileNameOnly + "." + levelNames[i] + f.fullLogWriter.suffix 67 | jsonMap["level"] = i 68 | bs, _ := json.Marshal(jsonMap) 69 | writer = newFileWriter().(*fileLogWriter) 70 | writer.Init(string(bs)) 71 | f.writers[i] = writer 72 | } 73 | } 74 | } 75 | 76 | return nil 77 | } 78 | 79 | func (f *multiFileLogWriter) Destroy() { 80 | for i := 0; i < len(f.writers); i++ { 81 | if f.writers[i] != nil { 82 | f.writers[i].Destroy() 83 | } 84 | } 85 | } 86 | 87 | func (f *multiFileLogWriter) WriteMsg(when time.Time, msg string, level int) error { 88 | if f.fullLogWriter != nil { 89 | f.fullLogWriter.WriteMsg(when, msg, level) 90 | } 91 | for i := 0; i < len(f.writers)-1; i++ { 92 | if f.writers[i] != nil { 93 | if level == f.writers[i].Level { 94 | f.writers[i].WriteMsg(when, msg, level) 95 | } 96 | } 97 | } 98 | return nil 99 | } 100 | 101 | func (f *multiFileLogWriter) Flush() { 102 | for i := 0; i < len(f.writers); i++ { 103 | if f.writers[i] != nil { 104 | f.writers[i].Flush() 105 | } 106 | } 107 | } 108 | 109 | // newFilesWriter create a FileLogWriter returning as LoggerInterface. 110 | func newFilesWriter() Logger { 111 | return &multiFileLogWriter{} 112 | } 113 | 114 | func init() { 115 | Register(AdapterMultiFile, newFilesWriter) 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 12 | // if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 16 | if err != nil { 17 | return -1 18 | } 19 | handler(w, req) 20 | return w.Code 21 | } 22 | 23 | // HTTPSuccess asserts that a specified handler returns a success status code. 24 | // 25 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 26 | // 27 | // Returns whether the assertion was successful (true) or not (false). 28 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 29 | code := httpCode(handler, method, url, values) 30 | if code == -1 { 31 | return false 32 | } 33 | return code >= http.StatusOK && code <= http.StatusPartialContent 34 | } 35 | 36 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 37 | // 38 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 39 | // 40 | // Returns whether the assertion was successful (true) or not (false). 41 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 42 | code := httpCode(handler, method, url, values) 43 | if code == -1 { 44 | return false 45 | } 46 | return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 47 | } 48 | 49 | // HTTPError asserts that a specified handler returns an error status code. 50 | // 51 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 52 | // 53 | // Returns whether the assertion was successful (true) or not (false). 54 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 55 | code := httpCode(handler, method, url, values) 56 | if code == -1 { 57 | return false 58 | } 59 | return code >= http.StatusBadRequest 60 | } 61 | 62 | // HTTPBody is a helper that returns HTTP body of the response. It returns 63 | // empty string if building a new request fails. 64 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 65 | w := httptest.NewRecorder() 66 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 67 | if err != nil { 68 | return "" 69 | } 70 | handler(w, req) 71 | return w.Body.String() 72 | } 73 | 74 | // HTTPBodyContains asserts that a specified handler returns a 75 | // body that contains a string. 76 | // 77 | // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 78 | // 79 | // Returns whether the assertion was successful (true) or not (false). 80 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 81 | body := HTTPBody(handler, method, url, values) 82 | 83 | contains := strings.Contains(body, fmt.Sprint(str)) 84 | if !contains { 85 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 86 | } 87 | 88 | return contains 89 | } 90 | 91 | // HTTPBodyNotContains asserts that a specified handler returns a 92 | // body that does not contain a string. 93 | // 94 | // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 95 | // 96 | // Returns whether the assertion was successful (true) or not (false). 97 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 98 | body := HTTPBody(handler, method, url, values) 99 | 100 | contains := strings.Contains(body, fmt.Sprint(str)) 101 | if contains { 102 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 103 | } 104 | 105 | return !contains 106 | } 107 | -------------------------------------------------------------------------------- /src/models/client/process_udp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package client 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | "sync" 22 | "time" 23 | 24 | "github.com/fatedier/frp/src/models/msg" 25 | "github.com/fatedier/frp/src/utils/conn" 26 | "github.com/fatedier/frp/src/utils/pool" 27 | ) 28 | 29 | type UdpProcesser struct { 30 | tcpConn *conn.Conn 31 | closeCh chan struct{} 32 | 33 | localAddr string 34 | 35 | // cache local udp connections 36 | // key is remoteAddr 37 | localUdpConns map[string]*net.UDPConn 38 | mutex sync.RWMutex 39 | tcpConnMutex sync.RWMutex 40 | } 41 | 42 | func NewUdpProcesser(c *conn.Conn, localIp string, localPort int64) *UdpProcesser { 43 | return &UdpProcesser{ 44 | tcpConn: c, 45 | closeCh: make(chan struct{}), 46 | localAddr: fmt.Sprintf("%s:%d", localIp, localPort), 47 | localUdpConns: make(map[string]*net.UDPConn), 48 | } 49 | } 50 | 51 | func (up *UdpProcesser) UpdateTcpConn(c *conn.Conn) { 52 | up.tcpConnMutex.Lock() 53 | defer up.tcpConnMutex.Unlock() 54 | up.tcpConn = c 55 | } 56 | 57 | func (up *UdpProcesser) Run() { 58 | go up.ReadLoop() 59 | } 60 | 61 | func (up *UdpProcesser) ReadLoop() { 62 | var ( 63 | buf string 64 | err error 65 | ) 66 | for { 67 | udpPacket := &msg.UdpPacket{} 68 | 69 | // read udp package from frps 70 | buf, err = up.tcpConn.ReadLine() 71 | if err != nil { 72 | if err == io.EOF { 73 | return 74 | } else { 75 | continue 76 | } 77 | } 78 | err = udpPacket.UnPack([]byte(buf)) 79 | if err != nil { 80 | continue 81 | } 82 | 83 | // write to local udp port 84 | sendConn, ok := up.GetUdpConn(udpPacket.SrcStr) 85 | if !ok { 86 | dstAddr, err := net.ResolveUDPAddr("udp", up.localAddr) 87 | if err != nil { 88 | continue 89 | } 90 | sendConn, err = net.DialUDP("udp", nil, dstAddr) 91 | if err != nil { 92 | continue 93 | } 94 | 95 | up.SetUdpConn(udpPacket.SrcStr, sendConn) 96 | } 97 | 98 | _, err = sendConn.Write(udpPacket.Content) 99 | if err != nil { 100 | sendConn.Close() 101 | continue 102 | } 103 | 104 | if !ok { 105 | go up.Forward(udpPacket, sendConn) 106 | } 107 | } 108 | } 109 | 110 | func (up *UdpProcesser) Forward(udpPacket *msg.UdpPacket, singleConn *net.UDPConn) { 111 | addr := udpPacket.SrcStr 112 | defer up.RemoveUdpConn(addr) 113 | 114 | buf := pool.GetBuf(2048) 115 | for { 116 | singleConn.SetReadDeadline(time.Now().Add(120 * time.Second)) 117 | n, remoteAddr, err := singleConn.ReadFromUDP(buf) 118 | if err != nil { 119 | return 120 | } 121 | 122 | // forward to frps 123 | forwardPacket := msg.NewUdpPacket(buf[0:n], remoteAddr, udpPacket.Src) 124 | up.tcpConnMutex.RLock() 125 | err = up.tcpConn.WriteString(string(forwardPacket.Pack()) + "\n") 126 | up.tcpConnMutex.RUnlock() 127 | if err != nil { 128 | return 129 | } 130 | } 131 | } 132 | 133 | func (up *UdpProcesser) GetUdpConn(addr string) (singleConn *net.UDPConn, ok bool) { 134 | up.mutex.RLock() 135 | defer up.mutex.RUnlock() 136 | singleConn, ok = up.localUdpConns[addr] 137 | return 138 | } 139 | 140 | func (up *UdpProcesser) SetUdpConn(addr string, conn *net.UDPConn) { 141 | up.mutex.Lock() 142 | defer up.mutex.Unlock() 143 | up.localUdpConns[addr] = conn 144 | } 145 | 146 | func (up *UdpProcesser) RemoveUdpConn(addr string) { 147 | up.mutex.Lock() 148 | defer up.mutex.Unlock() 149 | if c, ok := up.localUdpConns[addr]; ok { 150 | c.Close() 151 | } 152 | delete(up.localUdpConns, addr) 153 | } 154 | -------------------------------------------------------------------------------- /vendor/github.com/rakyll/statik/fs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package contains an HTTP file system that works with zip contents. 16 | package fs 17 | 18 | import ( 19 | "archive/zip" 20 | "bytes" 21 | "errors" 22 | "io" 23 | "io/ioutil" 24 | "net/http" 25 | "os" 26 | "strings" 27 | "sync" 28 | ) 29 | 30 | var zipData string 31 | 32 | type statikFS struct { 33 | files map[string]*zip.File 34 | } 35 | 36 | // Registers zip contents data, later used to initialize 37 | // the statik file system. 38 | func Register(data string) { 39 | zipData = data 40 | } 41 | 42 | // Creates a new file system with the registered zip contents data. 43 | func New() (http.FileSystem, error) { 44 | if zipData == "" { 45 | return nil, errors.New("statik/fs: No zip data registered.") 46 | } 47 | zipReader, err := zip.NewReader(strings.NewReader(zipData), int64(len(zipData))) 48 | if err != nil { 49 | return nil, err 50 | } 51 | files := make(map[string]*zip.File) 52 | for _, file := range zipReader.File { 53 | files["/"+file.Name] = file 54 | } 55 | return &statikFS{files: files}, nil 56 | } 57 | 58 | // Opens a file, unzip the contents and initializes 59 | // readers. Returns os.ErrNotExists if file is not 60 | // found in the archive. 61 | func (fs *statikFS) Open(name string) (http.File, error) { 62 | name = strings.Replace(name, "//", "/", -1) 63 | f, ok := fs.files[name] 64 | 65 | // The file doesn't match, but maybe it's a directory, 66 | // thus we should look for index.html 67 | if !ok { 68 | indexName := strings.Replace(name+"/index.html", "//", "/", -1) 69 | f, ok = fs.files[indexName] 70 | 71 | if !ok { 72 | return nil, os.ErrNotExist 73 | } 74 | 75 | return newFile(f, true) 76 | } 77 | return newFile(f, false) 78 | } 79 | 80 | var nopCloser = ioutil.NopCloser(nil) 81 | 82 | func newFile(zf *zip.File, isDir bool) (*file, error) { 83 | rc, err := zf.Open() 84 | if err != nil { 85 | return nil, err 86 | } 87 | defer rc.Close() 88 | all, err := ioutil.ReadAll(rc) 89 | if err != nil { 90 | return nil, err 91 | } 92 | return &file{ 93 | FileInfo: zf.FileInfo(), 94 | data: all, 95 | readerAt: bytes.NewReader(all), 96 | Closer: nopCloser, 97 | isDir: isDir, 98 | }, nil 99 | } 100 | 101 | // Represents an HTTP file, acts as a bridge between 102 | // zip.File and http.File. 103 | type file struct { 104 | os.FileInfo 105 | io.Closer 106 | 107 | data []byte // non-nil if regular file 108 | reader *io.SectionReader 109 | readerAt io.ReaderAt // over data 110 | isDir bool 111 | 112 | once sync.Once 113 | } 114 | 115 | func (f *file) newReader() { 116 | f.reader = io.NewSectionReader(f.readerAt, 0, f.FileInfo.Size()) 117 | } 118 | 119 | // Reads bytes into p, returns the number of read bytes. 120 | func (f *file) Read(p []byte) (n int, err error) { 121 | f.once.Do(f.newReader) 122 | return f.reader.Read(p) 123 | } 124 | 125 | // Seeks to the offset. 126 | func (f *file) Seek(offset int64, whence int) (ret int64, err error) { 127 | f.once.Do(f.newReader) 128 | return f.reader.Seek(offset, whence) 129 | } 130 | 131 | // Stats the file. 132 | func (f *file) Stat() (os.FileInfo, error) { 133 | return f, nil 134 | } 135 | 136 | // IsDir returns true if the file location represents a directory. 137 | func (f *file) IsDir() bool { 138 | return f.isDir 139 | } 140 | 141 | // Returns an empty slice of files, directory 142 | // listing is disabled. 143 | func (f *file) Readdir(count int) ([]os.FileInfo, error) { 144 | // directory listing is disabled. 145 | return make([]os.FileInfo, 0), nil 146 | } 147 | -------------------------------------------------------------------------------- /src/utils/pcrypto/pcrypto.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pcrypto 16 | 17 | import ( 18 | "bytes" 19 | "compress/gzip" 20 | "crypto/aes" 21 | "crypto/cipher" 22 | "crypto/md5" 23 | "crypto/rand" 24 | "encoding/hex" 25 | "fmt" 26 | "io" 27 | "io/ioutil" 28 | ) 29 | 30 | type Pcrypto struct { 31 | pkey []byte 32 | paes cipher.Block 33 | } 34 | 35 | func (pc *Pcrypto) Init(key []byte) error { 36 | var err error 37 | pc.pkey = pkKeyPadding(key) 38 | pc.paes, err = aes.NewCipher(pc.pkey) 39 | return err 40 | } 41 | 42 | func (pc *Pcrypto) Encrypt(src []byte) ([]byte, error) { 43 | // aes 44 | src = pKCS5Padding(src, aes.BlockSize) 45 | ciphertext := make([]byte, aes.BlockSize+len(src)) 46 | 47 | // The IV needs to be unique, but not secure. Therefore it's common to 48 | // include it at the beginning of the ciphertext. 49 | iv := ciphertext[:aes.BlockSize] 50 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 51 | return nil, err 52 | } 53 | blockMode := cipher.NewCBCEncrypter(pc.paes, iv) 54 | blockMode.CryptBlocks(ciphertext[aes.BlockSize:], src) 55 | return ciphertext, nil 56 | } 57 | 58 | func (pc *Pcrypto) Decrypt(str []byte) ([]byte, error) { 59 | // aes 60 | ciphertext, err := hex.DecodeString(fmt.Sprintf("%x", str)) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | if len(ciphertext) < aes.BlockSize { 66 | return nil, fmt.Errorf("ciphertext too short") 67 | } 68 | iv := ciphertext[:aes.BlockSize] 69 | ciphertext = ciphertext[aes.BlockSize:] 70 | 71 | if len(ciphertext)%aes.BlockSize != 0 { 72 | return nil, fmt.Errorf("crypto/cipher: ciphertext is not a multiple of the block size") 73 | } 74 | 75 | blockMode := cipher.NewCBCDecrypter(pc.paes, iv) 76 | blockMode.CryptBlocks(ciphertext, ciphertext) 77 | return pKCS5UnPadding(ciphertext), nil 78 | } 79 | 80 | func (pc *Pcrypto) Compression(src []byte) ([]byte, error) { 81 | var zbuf bytes.Buffer 82 | zwr, err := gzip.NewWriterLevel(&zbuf, gzip.DefaultCompression) 83 | if err != nil { 84 | return nil, err 85 | } 86 | defer zwr.Close() 87 | zwr.Write(src) 88 | zwr.Flush() 89 | return zbuf.Bytes(), nil 90 | } 91 | 92 | func (pc *Pcrypto) Decompression(src []byte) ([]byte, error) { 93 | zbuf := bytes.NewBuffer(src) 94 | zrd, err := gzip.NewReader(zbuf) 95 | if err != nil { 96 | return nil, err 97 | } 98 | defer zrd.Close() 99 | str, _ := ioutil.ReadAll(zrd) 100 | return str, nil 101 | } 102 | 103 | func pkKeyPadding(key []byte) []byte { 104 | l := len(key) 105 | if l == 16 || l == 24 || l == 32 { 106 | return key 107 | } 108 | if l < 16 { 109 | return append(key, bytes.Repeat([]byte{byte(0)}, 16-l)...) 110 | } else if l < 24 { 111 | return append(key, bytes.Repeat([]byte{byte(0)}, 24-l)...) 112 | } else if l < 32 { 113 | return append(key, bytes.Repeat([]byte{byte(0)}, 32-l)...) 114 | } else { 115 | md5Ctx := md5.New() 116 | md5Ctx.Write(key) 117 | md5Str := md5Ctx.Sum(nil) 118 | return []byte(hex.EncodeToString(md5Str)) 119 | } 120 | } 121 | 122 | func pKCS5Padding(ciphertext []byte, blockSize int) []byte { 123 | padding := blockSize - len(ciphertext)%blockSize 124 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 125 | return append(ciphertext, padtext...) 126 | } 127 | 128 | func pKCS5UnPadding(origData []byte) []byte { 129 | length := len(origData) 130 | unpadding := int(origData[length-1]) 131 | return origData[:(length - unpadding)] 132 | } 133 | 134 | func GetAuthKey(str string) (authKey string) { 135 | md5Ctx := md5.New() 136 | md5Ctx.Write([]byte(str)) 137 | md5Str := md5Ctx.Sum(nil) 138 | return hex.EncodeToString(md5Str) 139 | } 140 | -------------------------------------------------------------------------------- /doc/quick_start_en.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | frp is easier to use compared with other similar projects. 4 | 5 | We will use two simple demo to demonstrate how to use frp. 6 | 7 | 1. How to create a connection to **server A**'s **ssh port** by **server B** with **public IP address** x.x.x.x(replace to the real IP address of your server). 8 | 2. How to visit web service in **server A**'s **8000 port** and **8001 port** by **web01.yourdomain.com** and **web02.yourdomain.com** through **server B** with public ID address. 9 | 10 | ### Download SourceCode 11 | 12 | `go get github.com/fatedier/frp` is recommended, then the code will be copied to the directory `$GOPATH/src/github.com/fatedier/frp`. 13 | 14 | Or you can use `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp`. 15 | 16 | If you want to try it quickly, download the compiled program and configuration files from [https://github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases). 17 | 18 | ### Compile 19 | 20 | Enter the root directory and execute `make`, then wait until finished. 21 | 22 | **bin** include all executable programs when **conf** include corresponding configuration files. 23 | 24 | ### Pre-requirement 25 | 26 | * Go environment. Version of go >= 1.4. 27 | * Godep (if not exist, `go get` will be executed to download godep when compiling) 28 | 29 | ### Deploy 30 | 31 | 1. Move `./bin/frps` and `./conf/frps.ini` to any directory of **server B**. 32 | 2. Move `./bin/frpc` and `./conf/frpc.ini` to any directory of **server A**. 33 | 3. Modify all configuration files, details in next paragraph. 34 | 4. Execute `nohup ./frps &` or `nohup ./frps -c ./frps.ini &` in **server B**. 35 | 5. Execute `nohup ./frpc &` or `nohup ./frpc -c ./frpc.ini &` in **server A**. 36 | 6. Use `ssh -oPort=6000 {user}@x.x.x.x` to test if frp is work(replace {user} to real username in **server A**), or visit custom domains by browser. 37 | 38 | ## Tcp port forwarding 39 | 40 | ### Configuration files 41 | 42 | #### frps.ini 43 | 44 | ```ini 45 | [common] 46 | bind_addr = 0.0.0.0 47 | # for accept connections from frpc 48 | bind_port = 7000 49 | log_file = ./frps.log 50 | log_level = info 51 | 52 | # ssh is the custom name of proxy and there can be many proxies with unique name in one configure file 53 | [ssh] 54 | auth_token = 123 55 | bind_addr = 0.0.0.0 56 | # finally we connect to server A by this port 57 | listen_port = 6000 58 | ``` 59 | 60 | #### frpc.ini 61 | 62 | ```ini 63 | [common] 64 | # server address of frps 65 | server_addr = x.x.x.x 66 | server_port = 7000 67 | log_file = ./frpc.log 68 | log_level = info 69 | # for authentication 70 | auth_token = 123 71 | 72 | # ssh is proxy name same with configure in frps.ini 73 | [ssh] 74 | # local port which need to be transferred 75 | local_port = 22 76 | # if use_encryption equals true, messages between frpc and frps will be encrypted, default is false 77 | use_encryption = true 78 | ``` 79 | 80 | ## Http port forwarding and Custom domains binding 81 | 82 | If you only want to forward port one by one, you just need refer to [Tcp port forwarding](/doc/quick_start_en.md#Tcp-port-forwarding).If you want to visit different web pages deployed in different web servers by **server B**'s **80 port**, you should specify the type as **http**. 83 | 84 | You also need to resolve your **A record** of your custom domain to [server_addr], or resolve your **CNAME record** to [server_addr] if [server_addr] is a domain. 85 | 86 | After that, you can visit your web pages in local server by custom domains. 87 | 88 | ### Configuration files 89 | 90 | #### frps.ini 91 | 92 | ```ini 93 | [common] 94 | bind_addr = 0.0.0.0 95 | bind_port = 7000 96 | # if you want to support vhost, specify one port for http services 97 | vhost_http_port = 80 98 | log_file = ./frps.log 99 | log_level = info 100 | 101 | [web01] 102 | type = http 103 | auth_token = 123 104 | # # if proxy type equals http, custom_domains must be set separated by commas 105 | custom_domains = web01.yourdomain.com 106 | 107 | [web02] 108 | type = http 109 | auth_token = 123 110 | custom_domains = web02.yourdomain.com 111 | ``` 112 | 113 | #### frpc.ini 114 | 115 | ```ini 116 | [common] 117 | server_addr = x.x.x.x 118 | server_port = 7000 119 | log_file = ./frpc.log 120 | log_level = info 121 | auth_token = 123 122 | 123 | # custom domains are set in frps.ini 124 | [web01] 125 | type = http 126 | local_ip = 127.0.0.1 127 | local_port = 8000 128 | # encryption is optional, default is false 129 | use_encryption = true 130 | 131 | [web02] 132 | type = http 133 | local_ip = 127.0.0.1 134 | local_port = 8001 135 | ``` 136 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/smtp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logs 16 | 17 | import ( 18 | "crypto/tls" 19 | "encoding/json" 20 | "fmt" 21 | "net" 22 | "net/smtp" 23 | "strings" 24 | "time" 25 | ) 26 | 27 | // SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server. 28 | type SMTPWriter struct { 29 | Username string `json:"username"` 30 | Password string `json:"password"` 31 | Host string `json:"host"` 32 | Subject string `json:"subject"` 33 | FromAddress string `json:"fromAddress"` 34 | RecipientAddresses []string `json:"sendTos"` 35 | Level int `json:"level"` 36 | } 37 | 38 | // NewSMTPWriter create smtp writer. 39 | func newSMTPWriter() Logger { 40 | return &SMTPWriter{Level: LevelTrace} 41 | } 42 | 43 | // Init smtp writer with json config. 44 | // config like: 45 | // { 46 | // "username":"example@gmail.com", 47 | // "password:"password", 48 | // "host":"smtp.gmail.com:465", 49 | // "subject":"email title", 50 | // "fromAddress":"from@example.com", 51 | // "sendTos":["email1","email2"], 52 | // "level":LevelError 53 | // } 54 | func (s *SMTPWriter) Init(jsonconfig string) error { 55 | err := json.Unmarshal([]byte(jsonconfig), s) 56 | if err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | 62 | func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth { 63 | if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 { 64 | return nil 65 | } 66 | return smtp.PlainAuth( 67 | "", 68 | s.Username, 69 | s.Password, 70 | host, 71 | ) 72 | } 73 | 74 | func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error { 75 | client, err := smtp.Dial(hostAddressWithPort) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | host, _, _ := net.SplitHostPort(hostAddressWithPort) 81 | tlsConn := &tls.Config{ 82 | InsecureSkipVerify: true, 83 | ServerName: host, 84 | } 85 | if err = client.StartTLS(tlsConn); err != nil { 86 | return err 87 | } 88 | 89 | if auth != nil { 90 | if err = client.Auth(auth); err != nil { 91 | return err 92 | } 93 | } 94 | 95 | if err = client.Mail(fromAddress); err != nil { 96 | return err 97 | } 98 | 99 | for _, rec := range recipients { 100 | if err = client.Rcpt(rec); err != nil { 101 | return err 102 | } 103 | } 104 | 105 | w, err := client.Data() 106 | if err != nil { 107 | return err 108 | } 109 | _, err = w.Write([]byte(msgContent)) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | err = w.Close() 115 | if err != nil { 116 | return err 117 | } 118 | 119 | err = client.Quit() 120 | if err != nil { 121 | return err 122 | } 123 | 124 | return nil 125 | } 126 | 127 | // WriteMsg write message in smtp writer. 128 | // it will send an email with subject and only this message. 129 | func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { 130 | if level > s.Level { 131 | return nil 132 | } 133 | 134 | hp := strings.Split(s.Host, ":") 135 | 136 | // Set up authentication information. 137 | auth := s.getSMTPAuth(hp[0]) 138 | 139 | // Connect to the server, authenticate, set the sender and recipient, 140 | // and send the email all in one step. 141 | contentType := "Content-Type: text/plain" + "; charset=UTF-8" 142 | mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + 143 | ">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", when.Format("2006-01-02 15:04:05")) + msg) 144 | 145 | return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) 146 | } 147 | 148 | // Flush implementing method. empty. 149 | func (s *SMTPWriter) Flush() { 150 | return 151 | } 152 | 153 | // Destroy implementing method. empty. 154 | func (s *SMTPWriter) Destroy() { 155 | return 156 | } 157 | 158 | func init() { 159 | Register(AdapterMail, newSMTPWriter) 160 | } 161 | -------------------------------------------------------------------------------- /src/models/client/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package client 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "sync" 21 | "time" 22 | 23 | "github.com/fatedier/frp/src/models/config" 24 | "github.com/fatedier/frp/src/models/consts" 25 | "github.com/fatedier/frp/src/models/msg" 26 | "github.com/fatedier/frp/src/utils/conn" 27 | "github.com/fatedier/frp/src/utils/log" 28 | "github.com/fatedier/frp/src/utils/pcrypto" 29 | ) 30 | 31 | type ProxyClient struct { 32 | config.BaseConf 33 | LocalIp string 34 | LocalPort int64 35 | 36 | RemotePort int64 37 | CustomDomains []string 38 | Locations []string 39 | 40 | udpTunnel *conn.Conn 41 | once sync.Once 42 | closeFlag bool 43 | 44 | mutex sync.RWMutex 45 | } 46 | 47 | // if proxy type is udp, keep a tcp connection for transferring udp packages 48 | func (pc *ProxyClient) StartUdpTunnelOnce(addr string, port int64) { 49 | pc.once.Do(func() { 50 | var err error 51 | var c *conn.Conn 52 | udpProcessor := NewUdpProcesser(nil, pc.LocalIp, pc.LocalPort) 53 | for { 54 | if !pc.IsClosed() && (pc.udpTunnel == nil || pc.udpTunnel.IsClosed()) { 55 | if HttpProxy == "" { 56 | c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", addr, port)) 57 | } else { 58 | c, err = conn.ConnectServerByHttpProxy(HttpProxy, fmt.Sprintf("%s:%d", addr, port)) 59 | } 60 | if err != nil { 61 | log.Error("ProxyName [%s], udp tunnel connect to server [%s:%d] error, %v", pc.Name, addr, port, err) 62 | time.Sleep(10 * time.Second) 63 | continue 64 | } 65 | log.Info("ProxyName [%s], udp tunnel connect to server [%s:%d] success", pc.Name, addr, port) 66 | 67 | nowTime := time.Now().Unix() 68 | req := &msg.ControlReq{ 69 | Type: consts.NewWorkConnUdp, 70 | ProxyName: pc.Name, 71 | PrivilegeMode: pc.PrivilegeMode, 72 | Timestamp: nowTime, 73 | } 74 | if pc.PrivilegeMode == true { 75 | req.PrivilegeKey = pcrypto.GetAuthKey(pc.Name + PrivilegeToken + fmt.Sprintf("%d", nowTime)) 76 | } else { 77 | req.AuthKey = pcrypto.GetAuthKey(pc.Name + pc.AuthToken + fmt.Sprintf("%d", nowTime)) 78 | } 79 | 80 | buf, _ := json.Marshal(req) 81 | err = c.WriteString(string(buf) + "\n") 82 | if err != nil { 83 | log.Error("ProxyName [%s], udp tunnel write to server error, %v", pc.Name, err) 84 | c.Close() 85 | time.Sleep(1 * time.Second) 86 | continue 87 | } 88 | pc.mutex.Lock() 89 | pc.udpTunnel = c 90 | udpProcessor.UpdateTcpConn(pc.udpTunnel) 91 | pc.mutex.Unlock() 92 | 93 | udpProcessor.Run() 94 | } 95 | time.Sleep(1 * time.Second) 96 | } 97 | }) 98 | } 99 | 100 | func (pc *ProxyClient) CloseUdpTunnel() { 101 | pc.mutex.RLock() 102 | defer pc.mutex.RUnlock() 103 | if pc.udpTunnel != nil { 104 | pc.udpTunnel.Close() 105 | } 106 | } 107 | 108 | func (pc *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { 109 | c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", pc.LocalIp, pc.LocalPort)) 110 | if err != nil { 111 | log.Error("ProxyName [%s], connect to local port error, %v", pc.Name, err) 112 | } 113 | return 114 | } 115 | 116 | func (pc *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err error) { 117 | defer func() { 118 | if err != nil && c != nil { 119 | c.Close() 120 | } 121 | }() 122 | 123 | if HttpProxy == "" { 124 | c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", addr, port)) 125 | } else { 126 | c, err = conn.ConnectServerByHttpProxy(HttpProxy, fmt.Sprintf("%s:%d", addr, port)) 127 | } 128 | if err != nil { 129 | log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", pc.Name, addr, port, err) 130 | return 131 | } 132 | 133 | nowTime := time.Now().Unix() 134 | req := &msg.ControlReq{ 135 | Type: consts.NewWorkConn, 136 | ProxyName: pc.Name, 137 | PrivilegeMode: pc.PrivilegeMode, 138 | Timestamp: nowTime, 139 | } 140 | if pc.PrivilegeMode == true { 141 | req.PrivilegeKey = pcrypto.GetAuthKey(pc.Name + PrivilegeToken + fmt.Sprintf("%d", nowTime)) 142 | } else { 143 | req.AuthKey = pcrypto.GetAuthKey(pc.Name + pc.AuthToken + fmt.Sprintf("%d", nowTime)) 144 | } 145 | 146 | buf, _ := json.Marshal(req) 147 | err = c.WriteString(string(buf) + "\n") 148 | if err != nil { 149 | log.Error("ProxyName [%s], write to server error, %v", pc.Name, err) 150 | return 151 | } 152 | 153 | err = nil 154 | return 155 | } 156 | 157 | func (pc *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err error) { 158 | localConn, err := pc.GetLocalConn() 159 | if err != nil { 160 | return 161 | } 162 | remoteConn, err := pc.GetRemoteConn(serverAddr, serverPort) 163 | if err != nil { 164 | return 165 | } 166 | 167 | // l means local, r means remote 168 | log.Debug("Join two connections, (l[%s] r[%s]) (l[%s] r[%s])", localConn.GetLocalAddr(), localConn.GetRemoteAddr(), 169 | remoteConn.GetLocalAddr(), remoteConn.GetRemoteAddr()) 170 | needRecord := false 171 | go msg.JoinMore(localConn, remoteConn, pc.BaseConf, needRecord) 172 | 173 | return nil 174 | } 175 | 176 | func (pc *ProxyClient) SetCloseFlag(closeFlag bool) { 177 | pc.mutex.Lock() 178 | defer pc.mutex.Unlock() 179 | pc.closeFlag = closeFlag 180 | } 181 | 182 | func (pc *ProxyClient) IsClosed() bool { 183 | pc.mutex.RLock() 184 | defer pc.mutex.RUnlock() 185 | return pc.closeFlag 186 | } 187 | -------------------------------------------------------------------------------- /src/utils/vhost/https.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vhost 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | "strings" 22 | "time" 23 | 24 | "github.com/fatedier/frp/src/utils/conn" 25 | "github.com/fatedier/frp/src/utils/pool" 26 | ) 27 | 28 | const ( 29 | typeClientHello uint8 = 1 // Type client hello 30 | ) 31 | 32 | // TLS extension numbers 33 | const ( 34 | extensionServerName uint16 = 0 35 | extensionStatusRequest uint16 = 5 36 | extensionSupportedCurves uint16 = 10 37 | extensionSupportedPoints uint16 = 11 38 | extensionSignatureAlgorithms uint16 = 13 39 | extensionALPN uint16 = 16 40 | extensionSCT uint16 = 18 41 | extensionSessionTicket uint16 = 35 42 | extensionNextProtoNeg uint16 = 13172 // not IANA assigned 43 | extensionRenegotiationInfo uint16 = 0xff01 44 | ) 45 | 46 | type HttpsMuxer struct { 47 | *VhostMuxer 48 | } 49 | 50 | func NewHttpsMuxer(listener *conn.Listener, timeout time.Duration) (*HttpsMuxer, error) { 51 | mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, timeout) 52 | return &HttpsMuxer{mux}, err 53 | } 54 | 55 | func readHandshake(rd io.Reader) (host string, err error) { 56 | data := pool.GetBuf(1024) 57 | origin := data 58 | defer pool.PutBuf(origin) 59 | length, err := rd.Read(data) 60 | if err != nil { 61 | return 62 | } else { 63 | if length < 47 { 64 | err = fmt.Errorf("readHandshake: proto length[%d] is too short", length) 65 | return 66 | } 67 | } 68 | data = data[:length] 69 | if uint8(data[5]) != typeClientHello { 70 | err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5])) 71 | return 72 | } 73 | 74 | // session 75 | sessionIdLen := int(data[43]) 76 | if sessionIdLen > 32 || len(data) < 44+sessionIdLen { 77 | err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIdLen) 78 | return 79 | } 80 | data = data[44+sessionIdLen:] 81 | if len(data) < 2 { 82 | err = fmt.Errorf("readHandshake: dataLen[%d] after session is short", len(data)) 83 | return 84 | } 85 | 86 | // cipher suite numbers 87 | cipherSuiteLen := int(data[0])<<8 | int(data[1]) 88 | if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { 89 | err = fmt.Errorf("readHandshake: dataLen[%d] after cipher suite is short", len(data)) 90 | return 91 | } 92 | data = data[2+cipherSuiteLen:] 93 | if len(data) < 1 { 94 | err = fmt.Errorf("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen) 95 | return 96 | } 97 | 98 | // compression method 99 | compressionMethodsLen := int(data[0]) 100 | if len(data) < 1+compressionMethodsLen { 101 | err = fmt.Errorf("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen) 102 | return 103 | } 104 | 105 | data = data[1+compressionMethodsLen:] 106 | if len(data) == 0 { 107 | // ClientHello is optionally followed by extension data 108 | err = fmt.Errorf("readHandshake: there is no extension data to get servername") 109 | return 110 | } 111 | if len(data) < 2 { 112 | err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short") 113 | return 114 | } 115 | 116 | extensionsLength := int(data[0])<<8 | int(data[1]) 117 | data = data[2:] 118 | if extensionsLength != len(data) { 119 | err = fmt.Errorf("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data)) 120 | return 121 | } 122 | for len(data) != 0 { 123 | if len(data) < 4 { 124 | err = fmt.Errorf("readHandshake: extensionsDataLen[%d] is too short", len(data)) 125 | return 126 | } 127 | extension := uint16(data[0])<<8 | uint16(data[1]) 128 | length := int(data[2])<<8 | int(data[3]) 129 | data = data[4:] 130 | if len(data) < length { 131 | err = fmt.Errorf("readHandshake: extensionLen[%d] is long", length) 132 | return 133 | } 134 | 135 | switch extension { 136 | case extensionRenegotiationInfo: 137 | if length != 1 || data[0] != 0 { 138 | err = fmt.Errorf("readHandshake: extension reNegotiationInfoLen[%d] is short", length) 139 | return 140 | } 141 | case extensionNextProtoNeg: 142 | case extensionStatusRequest: 143 | case extensionServerName: 144 | d := data[:length] 145 | if len(d) < 2 { 146 | err = fmt.Errorf("readHandshake: remiaining dataLen[%d] is short", len(d)) 147 | return 148 | } 149 | namesLen := int(d[0])<<8 | int(d[1]) 150 | d = d[2:] 151 | if len(d) != namesLen { 152 | err = fmt.Errorf("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d)) 153 | return 154 | } 155 | for len(d) > 0 { 156 | if len(d) < 3 { 157 | err = fmt.Errorf("readHandshake: extension serverNameLen[%d] is short", len(d)) 158 | return 159 | } 160 | nameType := d[0] 161 | nameLen := int(d[1])<<8 | int(d[2]) 162 | d = d[3:] 163 | if len(d) < nameLen { 164 | err = fmt.Errorf("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d)) 165 | return 166 | } 167 | if nameType == 0 { 168 | serverName := string(d[:nameLen]) 169 | host = strings.TrimSpace(serverName) 170 | return host, nil 171 | } 172 | d = d[nameLen:] 173 | } 174 | } 175 | data = data[length:] 176 | } 177 | err = fmt.Errorf("Unknow error") 178 | return 179 | } 180 | 181 | func GetHttpsHostname(c *conn.Conn) (sc net.Conn, _ map[string]string, err error) { 182 | reqInfoMap := make(map[string]string, 0) 183 | sc, rd := newShareConn(c.TcpConn) 184 | host, err := readHandshake(rd) 185 | if err != nil { 186 | return sc, reqInfoMap, err 187 | } 188 | reqInfoMap["Host"] = host 189 | reqInfoMap["Scheme"] = "https" 190 | return sc, reqInfoMap, nil 191 | } 192 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build !js,!appengine,!safe,!disableunsafe 20 | 21 | package spew 22 | 23 | import ( 24 | "reflect" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | // UnsafeDisabled is a build-time constant which specifies whether or 30 | // not access to the unsafe package is available. 31 | UnsafeDisabled = false 32 | 33 | // ptrSize is the size of a pointer on the current arch. 34 | ptrSize = unsafe.Sizeof((*byte)(nil)) 35 | ) 36 | 37 | var ( 38 | // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 39 | // internal reflect.Value fields. These values are valid before golang 40 | // commit ecccf07e7f9d which changed the format. The are also valid 41 | // after commit 82f48826c6c7 which changed the format again to mirror 42 | // the original format. Code in the init function updates these offsets 43 | // as necessary. 44 | offsetPtr = uintptr(ptrSize) 45 | offsetScalar = uintptr(0) 46 | offsetFlag = uintptr(ptrSize * 2) 47 | 48 | // flagKindWidth and flagKindShift indicate various bits that the 49 | // reflect package uses internally to track kind information. 50 | // 51 | // flagRO indicates whether or not the value field of a reflect.Value is 52 | // read-only. 53 | // 54 | // flagIndir indicates whether the value field of a reflect.Value is 55 | // the actual data or a pointer to the data. 56 | // 57 | // These values are valid before golang commit 90a7c3c86944 which 58 | // changed their positions. Code in the init function updates these 59 | // flags as necessary. 60 | flagKindWidth = uintptr(5) 61 | flagKindShift = uintptr(flagKindWidth - 1) 62 | flagRO = uintptr(1 << 0) 63 | flagIndir = uintptr(1 << 1) 64 | ) 65 | 66 | func init() { 67 | // Older versions of reflect.Value stored small integers directly in the 68 | // ptr field (which is named val in the older versions). Versions 69 | // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 70 | // scalar for this purpose which unfortunately came before the flag 71 | // field, so the offset of the flag field is different for those 72 | // versions. 73 | // 74 | // This code constructs a new reflect.Value from a known small integer 75 | // and checks if the size of the reflect.Value struct indicates it has 76 | // the scalar field. When it does, the offsets are updated accordingly. 77 | vv := reflect.ValueOf(0xf00) 78 | if unsafe.Sizeof(vv) == (ptrSize * 4) { 79 | offsetScalar = ptrSize * 2 80 | offsetFlag = ptrSize * 3 81 | } 82 | 83 | // Commit 90a7c3c86944 changed the flag positions such that the low 84 | // order bits are the kind. This code extracts the kind from the flags 85 | // field and ensures it's the correct type. When it's not, the flag 86 | // order has been changed to the newer format, so the flags are updated 87 | // accordingly. 88 | upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 89 | upfv := *(*uintptr)(upf) 90 | flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { 92 | flagKindShift = 0 93 | flagRO = 1 << 5 94 | flagIndir = 1 << 6 95 | 96 | // Commit adf9b30e5594 modified the flags to separate the 97 | // flagRO flag into two bits which specifies whether or not the 98 | // field is embedded. This causes flagIndir to move over a bit 99 | // and means that flagRO is the combination of either of the 100 | // original flagRO bit and the new bit. 101 | // 102 | // This code detects the change by extracting what used to be 103 | // the indirect bit to ensure it's set. When it's not, the flag 104 | // order has been changed to the newer format, so the flags are 105 | // updated accordingly. 106 | if upfv&flagIndir == 0 { 107 | flagRO = 3 << 5 108 | flagIndir = 1 << 7 109 | } 110 | } 111 | } 112 | 113 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 114 | // the typical safety restrictions preventing access to unaddressable and 115 | // unexported data. It works by digging the raw pointer to the underlying 116 | // value out of the protected value and generating a new unprotected (unsafe) 117 | // reflect.Value to it. 118 | // 119 | // This allows us to check for implementations of the Stringer and error 120 | // interfaces to be used for pretty printing ordinarily unaddressable and 121 | // inaccessible values such as unexported struct fields. 122 | func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { 123 | indirects := 1 124 | vt := v.Type() 125 | upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 126 | rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 127 | if rvf&flagIndir != 0 { 128 | vt = reflect.PtrTo(v.Type()) 129 | indirects++ 130 | } else if offsetScalar != 0 { 131 | // The value is in the scalar field when it's not one of the 132 | // reference types. 133 | switch vt.Kind() { 134 | case reflect.Uintptr: 135 | case reflect.Chan: 136 | case reflect.Func: 137 | case reflect.Map: 138 | case reflect.Ptr: 139 | case reflect.UnsafePointer: 140 | default: 141 | upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 142 | offsetScalar) 143 | } 144 | } 145 | 146 | pv := reflect.NewAt(vt, upv) 147 | rv = pv 148 | for i := 0; i < indirects; i++ { 149 | rv = rv.Elem() 150 | } 151 | return rv 152 | } 153 | -------------------------------------------------------------------------------- /src/cmd/frps/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "encoding/base64" 19 | "encoding/json" 20 | "fmt" 21 | "io/ioutil" 22 | "net/http" 23 | "os" 24 | "strconv" 25 | "strings" 26 | "time" 27 | 28 | docopt "github.com/docopt/docopt-go" 29 | 30 | "github.com/fatedier/frp/src/assets" 31 | "github.com/fatedier/frp/src/models/server" 32 | "github.com/fatedier/frp/src/utils/conn" 33 | "github.com/fatedier/frp/src/utils/log" 34 | "github.com/fatedier/frp/src/utils/version" 35 | "github.com/fatedier/frp/src/utils/vhost" 36 | ) 37 | 38 | var usage string = `frps is the server of frp 39 | 40 | Usage: 41 | frps [-c config_file] [-L log_file] [--log-level=] [--addr=] 42 | frps [-c config_file] --reload 43 | frps -h | --help 44 | frps -v | --version 45 | 46 | Options: 47 | -c config_file set config file 48 | -L log_file set output log file, including console 49 | --log-level= set log level: debug, info, warn, error 50 | --addr= listen addr for client, example: 0.0.0.0:7000 51 | --reload reload ini file and configures in common section won't be changed 52 | -h --help show this screen 53 | -v --version show version 54 | ` 55 | 56 | func main() { 57 | // the configures parsed from file will be replaced by those from command line if exist 58 | args, err := docopt.Parse(usage, nil, true, version.Full(), false) 59 | 60 | if args["-c"] != nil { 61 | server.ConfigFile = args["-c"].(string) 62 | } 63 | err = server.LoadConf(server.ConfigFile) 64 | if err != nil { 65 | fmt.Println(err) 66 | os.Exit(-1) 67 | } 68 | 69 | // reload check 70 | if args["--reload"] != nil { 71 | if args["--reload"].(bool) { 72 | req, err := http.NewRequest("GET", "http://"+server.BindAddr+":"+fmt.Sprintf("%d", server.DashboardPort)+"/api/reload", nil) 73 | if err != nil { 74 | fmt.Printf("frps reload error: %v\n", err) 75 | os.Exit(1) 76 | } 77 | 78 | authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(server.DashboardUsername+":"+server.DashboardPassword)) 79 | 80 | req.Header.Add("Authorization", authStr) 81 | defaultClient := &http.Client{} 82 | resp, err := defaultClient.Do(req) 83 | 84 | if err != nil { 85 | fmt.Printf("frps reload error: %v\n", err) 86 | os.Exit(1) 87 | } else { 88 | defer resp.Body.Close() 89 | body, err := ioutil.ReadAll(resp.Body) 90 | if err != nil { 91 | fmt.Printf("frps reload error: %v\n", err) 92 | os.Exit(1) 93 | } 94 | res := &server.GeneralResponse{} 95 | err = json.Unmarshal(body, &res) 96 | if err != nil { 97 | fmt.Printf("http response error: %s\n", strings.TrimSpace(string(body))) 98 | os.Exit(1) 99 | } else if res.Code != 0 { 100 | fmt.Printf("reload error: %s\n", res.Msg) 101 | os.Exit(1) 102 | } 103 | fmt.Printf("reload success\n") 104 | os.Exit(0) 105 | } 106 | } 107 | } 108 | 109 | if args["-L"] != nil { 110 | if args["-L"].(string) == "console" { 111 | server.LogWay = "console" 112 | } else { 113 | server.LogWay = "file" 114 | server.LogFile = args["-L"].(string) 115 | } 116 | } 117 | 118 | if args["--log-level"] != nil { 119 | server.LogLevel = args["--log-level"].(string) 120 | } 121 | 122 | if args["--addr"] != nil { 123 | addr := strings.Split(args["--addr"].(string), ":") 124 | if len(addr) != 2 { 125 | fmt.Println("--addr format error: example 0.0.0.0:7000") 126 | os.Exit(1) 127 | } 128 | bindPort, err := strconv.ParseInt(addr[1], 10, 64) 129 | if err != nil { 130 | fmt.Println("--addr format error, example 0.0.0.0:7000") 131 | os.Exit(1) 132 | } 133 | server.BindAddr = addr[0] 134 | server.BindPort = bindPort 135 | } 136 | 137 | if args["-v"] != nil { 138 | if args["-v"].(bool) { 139 | fmt.Println(version.Full()) 140 | os.Exit(0) 141 | } 142 | } 143 | 144 | log.InitLog(server.LogWay, server.LogFile, server.LogLevel, server.LogMaxDays) 145 | 146 | // init assets 147 | err = assets.Load(server.AssetsDir) 148 | if err != nil { 149 | log.Error("Load assets error: %v", err) 150 | os.Exit(1) 151 | } 152 | 153 | l, err := conn.Listen(server.BindAddr, server.BindPort) 154 | if err != nil { 155 | log.Error("Create server listener error, %v", err) 156 | os.Exit(1) 157 | } 158 | 159 | // create vhost if VhostHttpPort != 0 160 | if server.VhostHttpPort != 0 { 161 | vhostListener, err := conn.Listen(server.BindAddr, server.VhostHttpPort) 162 | if err != nil { 163 | log.Error("Create vhost http listener error, %v", err) 164 | os.Exit(1) 165 | } 166 | server.VhostHttpMuxer, err = vhost.NewHttpMuxer(vhostListener, 30*time.Second) 167 | if err != nil { 168 | log.Error("Create vhost httpMuxer error, %v", err) 169 | } 170 | } 171 | 172 | // create vhost if VhostHttpPort != 0 173 | if server.VhostHttpsPort != 0 { 174 | vhostListener, err := conn.Listen(server.BindAddr, server.VhostHttpsPort) 175 | if err != nil { 176 | log.Error("Create vhost https listener error, %v", err) 177 | os.Exit(1) 178 | } 179 | server.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(vhostListener, 30*time.Second) 180 | if err != nil { 181 | log.Error("Create vhost httpsMuxer error, %v", err) 182 | } 183 | } 184 | 185 | // create dashboard web server if DashboardPort is set, so it won't be 0 186 | if server.DashboardPort != 0 { 187 | err := server.RunDashboardServer(server.BindAddr, server.DashboardPort) 188 | if err != nil { 189 | log.Error("Create dashboard web server error, %v", err) 190 | os.Exit(1) 191 | } 192 | } 193 | 194 | log.Info("Start frps success") 195 | if server.PrivilegeMode == true { 196 | log.Info("PrivilegeMode is enabled, you should pay more attention to security issues") 197 | } 198 | ProcessControlConn(l) 199 | } 200 | -------------------------------------------------------------------------------- /src/utils/vhost/vhost.go: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | package vhost 14 | 15 | import ( 16 | "bytes" 17 | "fmt" 18 | "io" 19 | "net" 20 | "strings" 21 | "sync" 22 | "time" 23 | 24 | "github.com/fatedier/frp/src/utils/conn" 25 | ) 26 | 27 | type muxFunc func(*conn.Conn) (net.Conn, map[string]string, error) 28 | type httpAuthFunc func(*conn.Conn, string, string, string) (bool, error) 29 | type hostRewriteFunc func(*conn.Conn, string) (net.Conn, error) 30 | 31 | type VhostMuxer struct { 32 | listener *conn.Listener 33 | timeout time.Duration 34 | vhostFunc muxFunc 35 | authFunc httpAuthFunc 36 | rewriteFunc hostRewriteFunc 37 | registryRouter *VhostRouters 38 | mutex sync.RWMutex 39 | } 40 | 41 | func NewVhostMuxer(listener *conn.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) { 42 | mux = &VhostMuxer{ 43 | listener: listener, 44 | timeout: timeout, 45 | vhostFunc: vhostFunc, 46 | authFunc: authFunc, 47 | rewriteFunc: rewriteFunc, 48 | registryRouter: NewVhostRouters(), 49 | } 50 | go mux.run() 51 | return mux, nil 52 | } 53 | 54 | // listen for a new domain name, if rewriteHost is not empty and rewriteFunc is not nil, then rewrite the host header to rewriteHost 55 | func (v *VhostMuxer) Listen(name, location, rewriteHost, userName, passWord string) (l *Listener, err error) { 56 | v.mutex.Lock() 57 | defer v.mutex.Unlock() 58 | 59 | _, ok := v.registryRouter.Exist(name, location) 60 | if ok { 61 | return nil, fmt.Errorf("hostname [%s] location [%s] is already registered", name, location) 62 | } 63 | 64 | l = &Listener{ 65 | name: name, 66 | location: location, 67 | rewriteHost: rewriteHost, 68 | userName: userName, 69 | passWord: passWord, 70 | mux: v, 71 | accept: make(chan *conn.Conn), 72 | } 73 | v.registryRouter.Add(name, location, l) 74 | return l, nil 75 | } 76 | 77 | func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) { 78 | v.mutex.RLock() 79 | defer v.mutex.RUnlock() 80 | 81 | // first we check the full hostname 82 | // if not exist, then check the wildcard_domain such as *.example.com 83 | vr, found := v.registryRouter.Get(name, path) 84 | if found { 85 | return vr.listener, true 86 | } 87 | 88 | domainSplit := strings.Split(name, ".") 89 | if len(domainSplit) < 3 { 90 | return l, false 91 | } 92 | domainSplit[0] = "*" 93 | name = strings.Join(domainSplit, ".") 94 | 95 | vr, found = v.registryRouter.Get(name, path) 96 | if !found { 97 | return 98 | } 99 | 100 | return vr.listener, true 101 | } 102 | 103 | func (v *VhostMuxer) run() { 104 | for { 105 | conn, err := v.listener.Accept() 106 | if err != nil { 107 | return 108 | } 109 | go v.handle(conn) 110 | } 111 | } 112 | 113 | func (v *VhostMuxer) handle(c *conn.Conn) { 114 | if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil { 115 | c.Close() 116 | return 117 | } 118 | 119 | sConn, reqInfoMap, err := v.vhostFunc(c) 120 | if err != nil { 121 | c.Close() 122 | return 123 | } 124 | 125 | name := strings.ToLower(reqInfoMap["Host"]) 126 | path := strings.ToLower(reqInfoMap["Path"]) 127 | l, ok := v.getListener(name, path) 128 | if !ok { 129 | c.Close() 130 | return 131 | } 132 | 133 | // if authFunc is exist and userName/password is set 134 | // verify user access 135 | if l.mux.authFunc != nil && 136 | l.userName != "" && l.passWord != "" { 137 | bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"]) 138 | if bAccess == false || err != nil { 139 | res := noAuthResponse() 140 | res.Write(c.TcpConn) 141 | c.Close() 142 | return 143 | } 144 | } 145 | 146 | if err = sConn.SetDeadline(time.Time{}); err != nil { 147 | c.Close() 148 | return 149 | } 150 | c.SetTcpConn(sConn) 151 | 152 | l.accept <- c 153 | } 154 | 155 | type Listener struct { 156 | name string 157 | location string 158 | rewriteHost string 159 | userName string 160 | passWord string 161 | mux *VhostMuxer // for closing VhostMuxer 162 | accept chan *conn.Conn 163 | } 164 | 165 | func (l *Listener) Accept() (*conn.Conn, error) { 166 | conn, ok := <-l.accept 167 | if !ok { 168 | return nil, fmt.Errorf("Listener closed") 169 | } 170 | 171 | // if rewriteFunc is exist and rewriteHost is set 172 | // rewrite http requests with a modified host header 173 | if l.mux.rewriteFunc != nil && l.rewriteHost != "" { 174 | sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost) 175 | if err != nil { 176 | return nil, fmt.Errorf("http host header rewrite failed") 177 | } 178 | conn.SetTcpConn(sConn) 179 | } 180 | 181 | return conn, nil 182 | } 183 | 184 | func (l *Listener) Close() error { 185 | l.mux.registryRouter.Del(l.name, l.location) 186 | close(l.accept) 187 | return nil 188 | } 189 | 190 | func (l *Listener) Name() string { 191 | return l.name 192 | } 193 | 194 | type sharedConn struct { 195 | net.Conn 196 | sync.Mutex 197 | buff *bytes.Buffer 198 | } 199 | 200 | // the bytes you read in io.Reader, will be reserved in sharedConn 201 | func newShareConn(conn net.Conn) (*sharedConn, io.Reader) { 202 | sc := &sharedConn{ 203 | Conn: conn, 204 | buff: bytes.NewBuffer(make([]byte, 0, 1024)), 205 | } 206 | return sc, io.TeeReader(conn, sc.buff) 207 | } 208 | 209 | func (sc *sharedConn) Read(p []byte) (n int, err error) { 210 | sc.Lock() 211 | if sc.buff == nil { 212 | sc.Unlock() 213 | return sc.Conn.Read(p) 214 | } 215 | sc.Unlock() 216 | n, err = sc.buff.Read(p) 217 | 218 | if err == io.EOF { 219 | sc.Lock() 220 | sc.buff = nil 221 | sc.Unlock() 222 | var n2 int 223 | n2, err = sc.Conn.Read(p[n:]) 224 | 225 | n += n2 226 | } 227 | return 228 | } 229 | 230 | func (sc *sharedConn) WriteBuff(buffer []byte) (err error) { 231 | sc.buff.Reset() 232 | _, err = sc.buff.Write(buffer) 233 | return err 234 | } 235 | -------------------------------------------------------------------------------- /vendor/github.com/astaxie/beego/logs/logger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 beego Author. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package logs 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "os" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | type logWriter struct { 26 | sync.Mutex 27 | writer io.Writer 28 | } 29 | 30 | func newLogWriter(wr io.Writer) *logWriter { 31 | return &logWriter{writer: wr} 32 | } 33 | 34 | func (lg *logWriter) println(when time.Time, msg string) { 35 | lg.Lock() 36 | h, _ := formatTimeHeader(when) 37 | lg.writer.Write(append(append(h, msg...), '\n')) 38 | lg.Unlock() 39 | } 40 | 41 | type outputMode int 42 | 43 | // DiscardNonColorEscSeq supports the divided color escape sequence. 44 | // But non-color escape sequence is not output. 45 | // Please use the OutputNonColorEscSeq If you want to output a non-color 46 | // escape sequences such as ncurses. However, it does not support the divided 47 | // color escape sequence. 48 | const ( 49 | _ outputMode = iota 50 | DiscardNonColorEscSeq 51 | OutputNonColorEscSeq 52 | ) 53 | 54 | // NewAnsiColorWriter creates and initializes a new ansiColorWriter 55 | // using io.Writer w as its initial contents. 56 | // In the console of Windows, which change the foreground and background 57 | // colors of the text by the escape sequence. 58 | // In the console of other systems, which writes to w all text. 59 | func NewAnsiColorWriter(w io.Writer) io.Writer { 60 | return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq) 61 | } 62 | 63 | // NewModeAnsiColorWriter create and initializes a new ansiColorWriter 64 | // by specifying the outputMode. 65 | func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer { 66 | if _, ok := w.(*ansiColorWriter); !ok { 67 | return &ansiColorWriter{ 68 | w: w, 69 | mode: mode, 70 | } 71 | } 72 | return w 73 | } 74 | 75 | const ( 76 | y1 = `0123456789` 77 | y2 = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789` 78 | y3 = `0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999` 79 | y4 = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789` 80 | mo1 = `000000000111` 81 | mo2 = `123456789012` 82 | d1 = `0000000001111111111222222222233` 83 | d2 = `1234567890123456789012345678901` 84 | h1 = `000000000011111111112222` 85 | h2 = `012345678901234567890123` 86 | mi1 = `000000000011111111112222222222333333333344444444445555555555` 87 | mi2 = `012345678901234567890123456789012345678901234567890123456789` 88 | s1 = `000000000011111111112222222222333333333344444444445555555555` 89 | s2 = `012345678901234567890123456789012345678901234567890123456789` 90 | ) 91 | 92 | func formatTimeHeader(when time.Time) ([]byte, int) { 93 | y, mo, d := when.Date() 94 | h, mi, s := when.Clock() 95 | //len("2006/01/02 15:04:05 ")==20 96 | var buf [20]byte 97 | 98 | buf[0] = y1[y/1000%10] 99 | buf[1] = y2[y/100] 100 | buf[2] = y3[y-y/100*100] 101 | buf[3] = y4[y-y/100*100] 102 | buf[4] = '/' 103 | buf[5] = mo1[mo-1] 104 | buf[6] = mo2[mo-1] 105 | buf[7] = '/' 106 | buf[8] = d1[d-1] 107 | buf[9] = d2[d-1] 108 | buf[10] = ' ' 109 | buf[11] = h1[h] 110 | buf[12] = h2[h] 111 | buf[13] = ':' 112 | buf[14] = mi1[mi] 113 | buf[15] = mi2[mi] 114 | buf[16] = ':' 115 | buf[17] = s1[s] 116 | buf[18] = s2[s] 117 | buf[19] = ' ' 118 | 119 | return buf[0:], d 120 | } 121 | 122 | var ( 123 | green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) 124 | white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) 125 | yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) 126 | red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) 127 | blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) 128 | magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) 129 | cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) 130 | 131 | w32Green = string([]byte{27, 91, 52, 50, 109}) 132 | w32White = string([]byte{27, 91, 52, 55, 109}) 133 | w32Yellow = string([]byte{27, 91, 52, 51, 109}) 134 | w32Red = string([]byte{27, 91, 52, 49, 109}) 135 | w32Blue = string([]byte{27, 91, 52, 52, 109}) 136 | w32Magenta = string([]byte{27, 91, 52, 53, 109}) 137 | w32Cyan = string([]byte{27, 91, 52, 54, 109}) 138 | 139 | reset = string([]byte{27, 91, 48, 109}) 140 | ) 141 | 142 | func ColorByStatus(cond bool, code int) string { 143 | switch { 144 | case code >= 200 && code < 300: 145 | return map[bool]string{true: green, false: w32Green}[cond] 146 | case code >= 300 && code < 400: 147 | return map[bool]string{true: white, false: w32White}[cond] 148 | case code >= 400 && code < 500: 149 | return map[bool]string{true: yellow, false: w32Yellow}[cond] 150 | default: 151 | return map[bool]string{true: red, false: w32Red}[cond] 152 | } 153 | } 154 | 155 | func ColorByMethod(cond bool, method string) string { 156 | switch method { 157 | case "GET": 158 | return map[bool]string{true: blue, false: w32Blue}[cond] 159 | case "POST": 160 | return map[bool]string{true: cyan, false: w32Cyan}[cond] 161 | case "PUT": 162 | return map[bool]string{true: yellow, false: w32Yellow}[cond] 163 | case "DELETE": 164 | return map[bool]string{true: red, false: w32Red}[cond] 165 | case "PATCH": 166 | return map[bool]string{true: green, false: w32Green}[cond] 167 | case "HEAD": 168 | return map[bool]string{true: magenta, false: w32Magenta}[cond] 169 | case "OPTIONS": 170 | return map[bool]string{true: white, false: w32White}[cond] 171 | default: 172 | return reset 173 | } 174 | } 175 | 176 | // Guard Mutex to guarantee atomicity of W32Debug(string) function 177 | var mu sync.Mutex 178 | 179 | // Helper method to output colored logs in Windows terminals 180 | func W32Debug(msg string) { 181 | mu.Lock() 182 | defer mu.Unlock() 183 | 184 | current := time.Now() 185 | w := NewAnsiColorWriter(os.Stdout) 186 | 187 | fmt.Fprintf(w, "[beego] %v %s\n", current.Format("2006/01/02 - 15:04:05"), msg) 188 | } 189 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /src/utils/vhost/http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vhost 16 | 17 | import ( 18 | "bufio" 19 | "bytes" 20 | "encoding/base64" 21 | "fmt" 22 | "io" 23 | "net" 24 | "net/http" 25 | "net/url" 26 | "strings" 27 | "time" 28 | 29 | "github.com/fatedier/frp/src/utils/conn" 30 | "github.com/fatedier/frp/src/utils/pool" 31 | ) 32 | 33 | type HttpMuxer struct { 34 | *VhostMuxer 35 | } 36 | 37 | func GetHttpRequestInfo(c *conn.Conn) (_ net.Conn, _ map[string]string, err error) { 38 | reqInfoMap := make(map[string]string, 0) 39 | sc, rd := newShareConn(c.TcpConn) 40 | 41 | request, err := http.ReadRequest(bufio.NewReader(rd)) 42 | if err != nil { 43 | return sc, reqInfoMap, err 44 | } 45 | // hostName 46 | tmpArr := strings.Split(request.Host, ":") 47 | reqInfoMap["Host"] = tmpArr[0] 48 | reqInfoMap["Path"] = request.URL.Path 49 | reqInfoMap["Scheme"] = request.URL.Scheme 50 | 51 | // Authorization 52 | authStr := request.Header.Get("Authorization") 53 | if authStr != "" { 54 | reqInfoMap["Authorization"] = authStr 55 | } 56 | request.Body.Close() 57 | return sc, reqInfoMap, nil 58 | } 59 | 60 | func NewHttpMuxer(listener *conn.Listener, timeout time.Duration) (*HttpMuxer, error) { 61 | mux, err := NewVhostMuxer(listener, GetHttpRequestInfo, HttpAuthFunc, HttpHostNameRewrite, timeout) 62 | return &HttpMuxer{mux}, err 63 | } 64 | 65 | func HttpHostNameRewrite(c *conn.Conn, rewriteHost string) (_ net.Conn, err error) { 66 | sc, rd := newShareConn(c.TcpConn) 67 | var buff []byte 68 | if buff, err = hostNameRewrite(rd, rewriteHost); err != nil { 69 | return sc, err 70 | } 71 | err = sc.WriteBuff(buff) 72 | return sc, err 73 | } 74 | 75 | func hostNameRewrite(request io.Reader, rewriteHost string) (_ []byte, err error) { 76 | buf := pool.GetBuf(1024) 77 | defer pool.PutBuf(buf) 78 | 79 | request.Read(buf) 80 | retBuffer, err := parseRequest(buf, rewriteHost) 81 | return retBuffer, err 82 | } 83 | 84 | func parseRequest(org []byte, rewriteHost string) (ret []byte, err error) { 85 | tp := bytes.NewBuffer(org) 86 | // First line: GET /index.html HTTP/1.0 87 | var b []byte 88 | if b, err = tp.ReadBytes('\n'); err != nil { 89 | return nil, err 90 | } 91 | req := new(http.Request) 92 | // we invoked ReadRequest in GetHttpHostname before, so we ignore error 93 | req.Method, req.RequestURI, req.Proto, _ = parseRequestLine(string(b)) 94 | rawurl := req.RequestURI 95 | // CONNECT www.google.com:443 HTTP/1.1 96 | justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") 97 | if justAuthority { 98 | rawurl = "http://" + rawurl 99 | } 100 | req.URL, _ = url.ParseRequestURI(rawurl) 101 | if justAuthority { 102 | // Strip the bogus "http://" back off. 103 | req.URL.Scheme = "" 104 | } 105 | 106 | // RFC2616: first case 107 | // GET /index.html HTTP/1.1 108 | // Host: www.google.com 109 | if req.URL.Host == "" { 110 | changedBuf, err := changeHostName(tp, rewriteHost) 111 | buf := new(bytes.Buffer) 112 | buf.Write(b) 113 | buf.Write(changedBuf) 114 | return buf.Bytes(), err 115 | } 116 | 117 | // RFC2616: second case 118 | // GET http://www.google.com/index.html HTTP/1.1 119 | // Host: doesntmatter 120 | // In this case, any Host line is ignored. 121 | hostPort := strings.Split(req.URL.Host, ":") 122 | if len(hostPort) == 1 { 123 | req.URL.Host = rewriteHost 124 | } else if len(hostPort) == 2 { 125 | req.URL.Host = fmt.Sprintf("%s:%s", rewriteHost, hostPort[1]) 126 | } 127 | firstLine := req.Method + " " + req.URL.String() + " " + req.Proto 128 | buf := new(bytes.Buffer) 129 | buf.WriteString(firstLine) 130 | tp.WriteTo(buf) 131 | return buf.Bytes(), err 132 | 133 | } 134 | 135 | // parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. 136 | func parseRequestLine(line string) (method, requestURI, proto string, ok bool) { 137 | s1 := strings.Index(line, " ") 138 | s2 := strings.Index(line[s1+1:], " ") 139 | if s1 < 0 || s2 < 0 { 140 | return 141 | } 142 | s2 += s1 + 1 143 | return line[:s1], line[s1+1 : s2], line[s2+1:], true 144 | } 145 | 146 | func changeHostName(buff *bytes.Buffer, rewriteHost string) (_ []byte, err error) { 147 | retBuf := new(bytes.Buffer) 148 | 149 | peek := buff.Bytes() 150 | for len(peek) > 0 { 151 | i := bytes.IndexByte(peek, '\n') 152 | if i < 3 { 153 | // Not present (-1) or found within the next few bytes, 154 | // implying we're at the end ("\r\n\r\n" or "\n\n") 155 | return nil, err 156 | } 157 | kv := peek[:i] 158 | j := bytes.IndexByte(kv, ':') 159 | if j < 0 { 160 | return nil, fmt.Errorf("malformed MIME header line: " + string(kv)) 161 | } 162 | if strings.Contains(strings.ToLower(string(kv[:j])), "host") { 163 | var hostHeader string 164 | portPos := bytes.IndexByte(kv[j+1:], ':') 165 | if portPos == -1 { 166 | hostHeader = fmt.Sprintf("Host: %s\n", rewriteHost) 167 | } else { 168 | hostHeader = fmt.Sprintf("Host: %s:%s\n", rewriteHost, kv[portPos+1:]) 169 | } 170 | retBuf.WriteString(hostHeader) 171 | peek = peek[i+1:] 172 | break 173 | } else { 174 | retBuf.Write(peek[:i]) 175 | retBuf.WriteByte('\n') 176 | } 177 | 178 | peek = peek[i+1:] 179 | } 180 | retBuf.Write(peek) 181 | return retBuf.Bytes(), err 182 | } 183 | 184 | func HttpAuthFunc(c *conn.Conn, userName, passWord, authorization string) (bAccess bool, err error) { 185 | s := strings.SplitN(authorization, " ", 2) 186 | if len(s) != 2 { 187 | res := noAuthResponse() 188 | res.Write(c.TcpConn) 189 | return 190 | } 191 | b, err := base64.StdEncoding.DecodeString(s[1]) 192 | if err != nil { 193 | return 194 | } 195 | pair := strings.SplitN(string(b), ":", 2) 196 | if len(pair) != 2 { 197 | return 198 | } 199 | if pair[0] != userName || pair[1] != passWord { 200 | return 201 | } 202 | return true, nil 203 | } 204 | 205 | func noAuthResponse() *http.Response { 206 | header := make(map[string][]string) 207 | header["WWW-Authenticate"] = []string{`Basic realm="Restricted"`} 208 | res := &http.Response{ 209 | Status: "401 Not authorized", 210 | StatusCode: 401, 211 | Proto: "HTTP/1.1", 212 | ProtoMajor: 1, 213 | ProtoMinor: 1, 214 | Header: header, 215 | } 216 | return res 217 | } 218 | -------------------------------------------------------------------------------- /src/models/metric/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "sort" 19 | "sync" 20 | "time" 21 | 22 | "github.com/fatedier/frp/src/models/consts" 23 | ) 24 | 25 | var ( 26 | DailyDataKeepDays int = 7 27 | ServerMetricInfoMap map[string]*ServerMetric 28 | smMutex sync.RWMutex 29 | ) 30 | 31 | type ServerMetric struct { 32 | Name string `json:"name"` 33 | Type string `json:"type"` 34 | BindAddr string `json:"bind_addr"` 35 | ListenPort int64 `json:"listen_port"` 36 | CustomDomains []string `json:"custom_domains"` 37 | Locations []string `json:"locations"` 38 | Status string `json:"status"` 39 | UseEncryption bool `json:"use_encryption"` 40 | UseGzip bool `json:"use_gzip"` 41 | PrivilegeMode bool `json:"privilege_mode"` 42 | 43 | // statistics 44 | CurrentConns int64 `json:"current_conns"` 45 | Daily []*DailyServerStats `json:"daily"` 46 | mutex sync.RWMutex 47 | } 48 | 49 | type DailyServerStats struct { 50 | Time string `json:"time"` 51 | FlowIn int64 `json:"flow_in"` 52 | FlowOut int64 `json:"flow_out"` 53 | TotalAcceptConns int64 `json:"total_accept_conns"` 54 | } 55 | 56 | // for sort 57 | type ServerMetricList []*ServerMetric 58 | 59 | func (l ServerMetricList) Len() int { return len(l) } 60 | func (l ServerMetricList) Less(i, j int) bool { return l[i].Name < l[j].Name } 61 | func (l ServerMetricList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 62 | 63 | func init() { 64 | ServerMetricInfoMap = make(map[string]*ServerMetric) 65 | } 66 | 67 | func (s *ServerMetric) clone() *ServerMetric { 68 | copy := *s 69 | copy.CustomDomains = make([]string, len(s.CustomDomains)) 70 | var i int 71 | for i = range copy.CustomDomains { 72 | copy.CustomDomains[i] = s.CustomDomains[i] 73 | } 74 | 75 | copy.Daily = make([]*DailyServerStats, len(s.Daily)) 76 | for i = range copy.Daily { 77 | tmpDaily := *s.Daily[i] 78 | copy.Daily[i] = &tmpDaily 79 | } 80 | return © 81 | } 82 | 83 | func GetAllProxyMetrics() []*ServerMetric { 84 | result := make(ServerMetricList, 0) 85 | smMutex.RLock() 86 | for _, metric := range ServerMetricInfoMap { 87 | metric.mutex.RLock() 88 | tmpMetric := metric.clone() 89 | metric.mutex.RUnlock() 90 | result = append(result, tmpMetric) 91 | } 92 | smMutex.RUnlock() 93 | 94 | // sort for result by proxy name 95 | sort.Sort(result) 96 | return result 97 | } 98 | 99 | // if proxyName isn't exist, return nil 100 | func GetProxyMetrics(proxyName string) *ServerMetric { 101 | smMutex.RLock() 102 | defer smMutex.RUnlock() 103 | metric, ok := ServerMetricInfoMap[proxyName] 104 | if ok { 105 | metric.mutex.RLock() 106 | tmpMetric := metric.clone() 107 | metric.mutex.RUnlock() 108 | return tmpMetric 109 | } else { 110 | return nil 111 | } 112 | } 113 | 114 | func SetProxyInfo(proxyName string, proxyType, bindAddr string, 115 | useEncryption, useGzip, privilegeMode bool, customDomains []string, 116 | locations []string, listenPort int64) { 117 | smMutex.Lock() 118 | info, ok := ServerMetricInfoMap[proxyName] 119 | if !ok { 120 | info = &ServerMetric{} 121 | info.Daily = make([]*DailyServerStats, 0) 122 | } 123 | info.Name = proxyName 124 | info.Type = proxyType 125 | info.UseEncryption = useEncryption 126 | info.UseGzip = useGzip 127 | info.PrivilegeMode = privilegeMode 128 | info.BindAddr = bindAddr 129 | info.ListenPort = listenPort 130 | info.CustomDomains = customDomains 131 | info.Locations = locations 132 | ServerMetricInfoMap[proxyName] = info 133 | smMutex.Unlock() 134 | } 135 | 136 | func SetStatus(proxyName string, status int64) { 137 | smMutex.RLock() 138 | metric, ok := ServerMetricInfoMap[proxyName] 139 | smMutex.RUnlock() 140 | if ok { 141 | metric.mutex.Lock() 142 | metric.Status = consts.StatusStr[status] 143 | metric.mutex.Unlock() 144 | } 145 | } 146 | 147 | type DealFuncType func(*DailyServerStats) 148 | 149 | func DealDailyData(dailyData []*DailyServerStats, fn DealFuncType) (newDailyData []*DailyServerStats) { 150 | now := time.Now().Format("20060102") 151 | dailyLen := len(dailyData) 152 | if dailyLen == 0 { 153 | daily := &DailyServerStats{} 154 | daily.Time = now 155 | fn(daily) 156 | dailyData = append(dailyData, daily) 157 | } else { 158 | daily := dailyData[dailyLen-1] 159 | if daily.Time == now { 160 | fn(daily) 161 | } else { 162 | newDaily := &DailyServerStats{} 163 | newDaily.Time = now 164 | fn(newDaily) 165 | if dailyLen == DailyDataKeepDays { 166 | for i := 0; i < dailyLen-1; i++ { 167 | dailyData[i] = dailyData[i+1] 168 | } 169 | dailyData[dailyLen-1] = newDaily 170 | } else { 171 | dailyData = append(dailyData, newDaily) 172 | } 173 | } 174 | } 175 | return dailyData 176 | } 177 | 178 | func OpenConnection(proxyName string) { 179 | smMutex.RLock() 180 | metric, ok := ServerMetricInfoMap[proxyName] 181 | smMutex.RUnlock() 182 | if ok { 183 | metric.mutex.Lock() 184 | metric.CurrentConns++ 185 | metric.Daily = DealDailyData(metric.Daily, func(stats *DailyServerStats) { 186 | stats.TotalAcceptConns++ 187 | }) 188 | metric.mutex.Unlock() 189 | } 190 | } 191 | 192 | func CloseConnection(proxyName string) { 193 | smMutex.RLock() 194 | metric, ok := ServerMetricInfoMap[proxyName] 195 | smMutex.RUnlock() 196 | if ok { 197 | metric.mutex.Lock() 198 | metric.CurrentConns-- 199 | metric.mutex.Unlock() 200 | } 201 | } 202 | 203 | func AddFlowIn(proxyName string, value int64) { 204 | smMutex.RLock() 205 | metric, ok := ServerMetricInfoMap[proxyName] 206 | smMutex.RUnlock() 207 | if ok { 208 | metric.mutex.Lock() 209 | metric.Daily = DealDailyData(metric.Daily, func(stats *DailyServerStats) { 210 | stats.FlowIn += value 211 | }) 212 | metric.mutex.Unlock() 213 | } 214 | } 215 | 216 | func AddFlowOut(proxyName string, value int64) { 217 | smMutex.RLock() 218 | metric, ok := ServerMetricInfoMap[proxyName] 219 | smMutex.RUnlock() 220 | if ok { 221 | metric.mutex.Lock() 222 | metric.Daily = DealDailyData(metric.Daily, func(stats *DailyServerStats) { 223 | stats.FlowOut += value 224 | }) 225 | metric.mutex.Unlock() 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/models/msg/process.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package msg 16 | 17 | import ( 18 | "bytes" 19 | "encoding/binary" 20 | "fmt" 21 | "io" 22 | "sync" 23 | 24 | "github.com/fatedier/frp/src/models/config" 25 | "github.com/fatedier/frp/src/models/metric" 26 | "github.com/fatedier/frp/src/utils/conn" 27 | "github.com/fatedier/frp/src/utils/log" 28 | "github.com/fatedier/frp/src/utils/pcrypto" 29 | "github.com/fatedier/frp/src/utils/pool" 30 | ) 31 | 32 | // deprecated 33 | // will block until connection close 34 | func Join(c1 *conn.Conn, c2 *conn.Conn) { 35 | var wait sync.WaitGroup 36 | pipe := func(to *conn.Conn, from *conn.Conn) { 37 | defer to.Close() 38 | defer from.Close() 39 | defer wait.Done() 40 | 41 | var err error 42 | _, err = io.Copy(to.TcpConn, from.TcpConn) 43 | if err != nil { 44 | log.Warn("join connections error, %v", err) 45 | } 46 | } 47 | 48 | wait.Add(2) 49 | go pipe(c1, c2) 50 | go pipe(c2, c1) 51 | wait.Wait() 52 | return 53 | } 54 | 55 | // join two connections and do some operations 56 | func JoinMore(c1 io.ReadWriteCloser, c2 io.ReadWriteCloser, conf config.BaseConf, needRecord bool) { 57 | var wait sync.WaitGroup 58 | encryptPipe := func(from io.ReadCloser, to io.WriteCloser) { 59 | defer from.Close() 60 | defer to.Close() 61 | defer wait.Done() 62 | 63 | // we don't care about errors here 64 | pipeEncrypt(from, to, conf, needRecord) 65 | } 66 | 67 | decryptPipe := func(to io.ReadCloser, from io.WriteCloser) { 68 | defer from.Close() 69 | defer to.Close() 70 | defer wait.Done() 71 | 72 | // we don't care about errors here 73 | pipeDecrypt(to, from, conf, needRecord) 74 | } 75 | 76 | if needRecord { 77 | metric.OpenConnection(conf.Name) 78 | } 79 | wait.Add(2) 80 | go encryptPipe(c1, c2) 81 | go decryptPipe(c2, c1) 82 | wait.Wait() 83 | if needRecord { 84 | metric.CloseConnection(conf.Name) 85 | } 86 | log.Debug("ProxyName [%s], One tunnel stopped", conf.Name) 87 | return 88 | } 89 | 90 | func pkgMsg(data []byte) []byte { 91 | llen := uint32(len(data)) 92 | buf := new(bytes.Buffer) 93 | binary.Write(buf, binary.BigEndian, llen) 94 | buf.Write(data) 95 | return buf.Bytes() 96 | } 97 | 98 | func unpkgMsg(data []byte) (int, []byte, []byte) { 99 | if len(data) < 4 { 100 | return -1, nil, data 101 | } 102 | llen := int(binary.BigEndian.Uint32(data[0:4])) 103 | // no complete 104 | if len(data) < llen+4 { 105 | return -1, nil, data 106 | } 107 | 108 | return 0, data[4 : llen+4], data[llen+4:] 109 | } 110 | 111 | // decrypt msg from reader, then write into writer 112 | func pipeDecrypt(r io.Reader, w io.Writer, conf config.BaseConf, needRecord bool) (err error) { 113 | laes := new(pcrypto.Pcrypto) 114 | key := conf.AuthToken 115 | if conf.PrivilegeMode { 116 | key = conf.PrivilegeToken 117 | } 118 | if err := laes.Init([]byte(key)); err != nil { 119 | log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) 120 | return fmt.Errorf("Pcrypto Init error: %v", err) 121 | } 122 | 123 | // get []byte from buffer pool 124 | buf := pool.GetBuf(5*1024 + 4) 125 | defer pool.PutBuf(buf) 126 | 127 | var left, res []byte 128 | var cnt int = -1 129 | 130 | // record 131 | var flowBytes int64 = 0 132 | if needRecord { 133 | defer func() { 134 | metric.AddFlowOut(conf.Name, flowBytes) 135 | }() 136 | } 137 | 138 | for { 139 | // there may be more than 1 package in variable 140 | // and we read more bytes if unpkgMsg returns an error 141 | var newBuf []byte 142 | if cnt < 0 { 143 | n, err := r.Read(buf) 144 | if err != nil { 145 | return err 146 | } 147 | newBuf = append(left, buf[0:n]...) 148 | } else { 149 | newBuf = left 150 | } 151 | cnt, res, left = unpkgMsg(newBuf) 152 | if cnt < 0 { 153 | // limit one package length, maximum is 1MB 154 | if len(res) > 1024*1024 { 155 | log.Warn("ProxyName [%s], package length exceeds the limit") 156 | return fmt.Errorf("package length error") 157 | } 158 | continue 159 | } 160 | 161 | // aes 162 | if conf.UseEncryption { 163 | res, err = laes.Decrypt(res) 164 | if err != nil { 165 | log.Warn("ProxyName [%s], decrypt error, %v", conf.Name, err) 166 | return fmt.Errorf("Decrypt error: %v", err) 167 | } 168 | } 169 | // gzip 170 | if conf.UseGzip { 171 | res, err = laes.Decompression(res) 172 | if err != nil { 173 | log.Warn("ProxyName [%s], decompression error, %v", conf.Name, err) 174 | return fmt.Errorf("Decompression error: %v", err) 175 | } 176 | } 177 | 178 | _, err = w.Write(res) 179 | if err != nil { 180 | return err 181 | } 182 | 183 | if needRecord { 184 | flowBytes += int64(len(res)) 185 | if flowBytes >= 1024*1024 { 186 | metric.AddFlowOut(conf.Name, flowBytes) 187 | flowBytes = 0 188 | } 189 | } 190 | } 191 | return nil 192 | } 193 | 194 | // recvive msg from reader, then encrypt msg into writer 195 | func pipeEncrypt(r io.Reader, w io.Writer, conf config.BaseConf, needRecord bool) (err error) { 196 | laes := new(pcrypto.Pcrypto) 197 | key := conf.AuthToken 198 | if conf.PrivilegeMode { 199 | key = conf.PrivilegeToken 200 | } 201 | if err := laes.Init([]byte(key)); err != nil { 202 | log.Warn("ProxyName [%s], Pcrypto Init error: %v", conf.Name, err) 203 | return fmt.Errorf("Pcrypto Init error: %v", err) 204 | } 205 | 206 | // record 207 | var flowBytes int64 = 0 208 | if needRecord { 209 | defer func() { 210 | metric.AddFlowIn(conf.Name, flowBytes) 211 | }() 212 | } 213 | 214 | // get []byte from buffer pool 215 | buf := pool.GetBuf(5*1024 + 4) 216 | defer pool.PutBuf(buf) 217 | 218 | for { 219 | n, err := r.Read(buf) 220 | if err != nil { 221 | return err 222 | } 223 | if needRecord { 224 | flowBytes += int64(n) 225 | if flowBytes >= 1024*1024 { 226 | metric.AddFlowIn(conf.Name, flowBytes) 227 | flowBytes = 0 228 | } 229 | } 230 | 231 | res := buf[0:n] 232 | // gzip 233 | if conf.UseGzip { 234 | res, err = laes.Compression(res) 235 | if err != nil { 236 | log.Warn("ProxyName [%s], compression error: %v", conf.Name, err) 237 | return fmt.Errorf("Compression error: %v", err) 238 | } 239 | } 240 | // aes 241 | if conf.UseEncryption { 242 | res, err = laes.Encrypt(res) 243 | if err != nil { 244 | log.Warn("ProxyName [%s], encrypt error: %v", conf.Name, err) 245 | return fmt.Errorf("Encrypt error: %v", err) 246 | } 247 | } 248 | 249 | res = pkgMsg(res) 250 | _, err = w.Write(res) 251 | if err != nil { 252 | return err 253 | } 254 | } 255 | 256 | return nil 257 | } 258 | -------------------------------------------------------------------------------- /src/cmd/frpc/control.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "io" 21 | "sync" 22 | "time" 23 | 24 | "github.com/fatedier/frp/src/models/client" 25 | "github.com/fatedier/frp/src/models/consts" 26 | "github.com/fatedier/frp/src/models/msg" 27 | "github.com/fatedier/frp/src/utils/conn" 28 | "github.com/fatedier/frp/src/utils/log" 29 | "github.com/fatedier/frp/src/utils/pcrypto" 30 | ) 31 | 32 | func ControlProcess(cli *client.ProxyClient, wait *sync.WaitGroup) { 33 | defer wait.Done() 34 | 35 | msgSendChan := make(chan interface{}, 1024) 36 | 37 | c, err := loginToServer(cli) 38 | if err != nil { 39 | log.Error("ProxyName [%s], connect to server failed!", cli.Name) 40 | return 41 | } 42 | defer c.Close() 43 | 44 | go heartbeatSender(c, msgSendChan) 45 | 46 | go msgSender(cli, c, msgSendChan) 47 | msgReader(cli, c, msgSendChan) 48 | 49 | close(msgSendChan) 50 | } 51 | 52 | // loop for reading messages from frpc after control connection is established 53 | func msgReader(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface{}) error { 54 | // for heartbeat 55 | var heartbeatTimeout bool = false 56 | timer := time.AfterFunc(time.Duration(client.HeartBeatTimeout)*time.Second, func() { 57 | heartbeatTimeout = true 58 | if c != nil { 59 | c.Close() 60 | } 61 | if cli != nil { 62 | // if it's not udp type, nothing will happen 63 | cli.CloseUdpTunnel() 64 | cli.SetCloseFlag(true) 65 | } 66 | log.Error("ProxyName [%s], heartbeatRes from frps timeout", cli.Name) 67 | }) 68 | defer timer.Stop() 69 | 70 | for { 71 | buf, err := c.ReadLine() 72 | if err == io.EOF || c.IsClosed() { 73 | timer.Stop() 74 | c.Close() 75 | cli.SetCloseFlag(true) 76 | log.Warn("ProxyName [%s], frps close this control conn!", cli.Name) 77 | var delayTime time.Duration = 1 78 | 79 | // loop until reconnect to frps 80 | for { 81 | log.Info("ProxyName [%s], try to reconnect to frps [%s:%d]...", cli.Name, client.ServerAddr, client.ServerPort) 82 | c, err = loginToServer(cli) 83 | if err == nil { 84 | close(msgSendChan) 85 | msgSendChan = make(chan interface{}, 1024) 86 | go heartbeatSender(c, msgSendChan) 87 | go msgSender(cli, c, msgSendChan) 88 | cli.SetCloseFlag(false) 89 | break 90 | } 91 | 92 | if delayTime < 30 { 93 | delayTime = delayTime * 2 94 | } else { 95 | delayTime = 30 96 | } 97 | time.Sleep(delayTime * time.Second) 98 | } 99 | continue 100 | } else if err != nil { 101 | log.Warn("ProxyName [%s], read from frps error: %v", cli.Name, err) 102 | continue 103 | } 104 | 105 | ctlRes := &msg.ControlRes{} 106 | if err := json.Unmarshal([]byte(buf), &ctlRes); err != nil { 107 | log.Warn("ProxyName [%s], parse msg from frps error: %v : %s", cli.Name, err, buf) 108 | continue 109 | } 110 | 111 | switch ctlRes.Type { 112 | case consts.HeartbeatRes: 113 | log.Debug("ProxyName [%s], receive heartbeat response", cli.Name) 114 | timer.Reset(time.Duration(client.HeartBeatTimeout) * time.Second) 115 | case consts.NoticeUserConn: 116 | log.Debug("ProxyName [%s], new user connection", cli.Name) 117 | // join local and remote connections, async 118 | go cli.StartTunnel(client.ServerAddr, client.ServerPort) 119 | default: 120 | log.Warn("ProxyName [%s}, unsupport msgType [%d]", cli.Name, ctlRes.Type) 121 | } 122 | } 123 | return nil 124 | } 125 | 126 | // loop for sending messages from channel to frps 127 | func msgSender(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface{}) { 128 | for { 129 | msg, ok := <-msgSendChan 130 | if !ok { 131 | break 132 | } 133 | 134 | buf, _ := json.Marshal(msg) 135 | err := c.WriteString(string(buf) + "\n") 136 | if err != nil { 137 | log.Warn("ProxyName [%s], write to server error, proxy exit", cli.Name) 138 | c.Close() 139 | break 140 | } 141 | } 142 | } 143 | 144 | func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { 145 | if client.HttpProxy == "" { 146 | c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", client.ServerAddr, client.ServerPort)) 147 | } else { 148 | c, err = conn.ConnectServerByHttpProxy(client.HttpProxy, fmt.Sprintf("%s:%d", client.ServerAddr, client.ServerPort)) 149 | } 150 | if err != nil { 151 | log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", cli.Name, client.ServerAddr, client.ServerPort, err) 152 | return 153 | } 154 | 155 | nowTime := time.Now().Unix() 156 | req := &msg.ControlReq{ 157 | Type: consts.NewCtlConn, 158 | ProxyName: cli.Name, 159 | UseEncryption: cli.UseEncryption, 160 | UseGzip: cli.UseGzip, 161 | PrivilegeMode: cli.PrivilegeMode, 162 | ProxyType: cli.Type, 163 | PoolCount: cli.PoolCount, 164 | HostHeaderRewrite: cli.HostHeaderRewrite, 165 | HttpUserName: cli.HttpUserName, 166 | HttpPassWord: cli.HttpPassWord, 167 | SubDomain: cli.SubDomain, 168 | Timestamp: nowTime, 169 | } 170 | if cli.PrivilegeMode { 171 | privilegeKey := pcrypto.GetAuthKey(cli.Name + client.PrivilegeToken + fmt.Sprintf("%d", nowTime)) 172 | req.RemotePort = cli.RemotePort 173 | req.CustomDomains = cli.CustomDomains 174 | req.Locations = cli.Locations 175 | req.PrivilegeKey = privilegeKey 176 | } else { 177 | authKey := pcrypto.GetAuthKey(cli.Name + cli.AuthToken + fmt.Sprintf("%d", nowTime)) 178 | req.AuthKey = authKey 179 | } 180 | 181 | buf, _ := json.Marshal(req) 182 | err = c.WriteString(string(buf) + "\n") 183 | if err != nil { 184 | log.Error("ProxyName [%s], write to server error, %v", cli.Name, err) 185 | return 186 | } 187 | 188 | res, err := c.ReadLine() 189 | if err != nil { 190 | log.Error("ProxyName [%s], read from server error, %v", cli.Name, err) 191 | return 192 | } 193 | log.Debug("ProxyName [%s], read [%s]", cli.Name, res) 194 | 195 | ctlRes := &msg.ControlRes{} 196 | if err = json.Unmarshal([]byte(res), &ctlRes); err != nil { 197 | log.Error("ProxyName [%s], format server response error, %v", cli.Name, err) 198 | return 199 | } 200 | 201 | if ctlRes.Code != 0 { 202 | log.Error("ProxyName [%s], start proxy error, %s", cli.Name, ctlRes.Msg) 203 | return c, fmt.Errorf("%s", ctlRes.Msg) 204 | } 205 | 206 | log.Info("ProxyName [%s], connect to server [%s:%d] success!", cli.Name, client.ServerAddr, client.ServerPort) 207 | 208 | if cli.Type == "udp" { 209 | // we only need one udp work connection 210 | // all udp messages will be forwarded throngh this connection 211 | go cli.StartUdpTunnelOnce(client.ServerAddr, client.ServerPort) 212 | } 213 | return 214 | } 215 | 216 | func heartbeatSender(c *conn.Conn, msgSendChan chan interface{}) { 217 | heartbeatReq := &msg.ControlReq{ 218 | Type: consts.HeartbeatReq, 219 | } 220 | log.Info("Start to send heartbeat to frps") 221 | for { 222 | time.Sleep(time.Duration(client.HeartBeatInterval) * time.Second) 223 | if c != nil && !c.IsClosed() { 224 | log.Debug("Send heartbeat to server") 225 | msgSendChan <- heartbeatReq 226 | } else { 227 | break 228 | } 229 | } 230 | log.Info("Heartbeat goroutine exit") 231 | } 232 | -------------------------------------------------------------------------------- /src/utils/conn/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package conn 16 | 17 | import ( 18 | "bufio" 19 | "bytes" 20 | "encoding/base64" 21 | "fmt" 22 | "io" 23 | "net" 24 | "net/http" 25 | "net/url" 26 | "strings" 27 | "sync" 28 | "time" 29 | 30 | "github.com/fatedier/frp/src/utils/pool" 31 | ) 32 | 33 | type Listener struct { 34 | addr net.Addr 35 | l *net.TCPListener 36 | accept chan *Conn 37 | closeFlag bool 38 | } 39 | 40 | func Listen(bindAddr string, bindPort int64) (l *Listener, err error) { 41 | tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort)) 42 | if err != nil { 43 | return l, err 44 | } 45 | listener, err := net.ListenTCP("tcp", tcpAddr) 46 | if err != nil { 47 | return l, err 48 | } 49 | 50 | l = &Listener{ 51 | addr: listener.Addr(), 52 | l: listener, 53 | accept: make(chan *Conn), 54 | closeFlag: false, 55 | } 56 | 57 | go func() { 58 | for { 59 | conn, err := l.l.AcceptTCP() 60 | if err != nil { 61 | if l.closeFlag { 62 | return 63 | } 64 | continue 65 | } 66 | 67 | c := NewConn(conn) 68 | l.accept <- c 69 | } 70 | }() 71 | return l, err 72 | } 73 | 74 | // wait util get one new connection or listener is closed 75 | // if listener is closed, err returned 76 | func (l *Listener) Accept() (*Conn, error) { 77 | conn, ok := <-l.accept 78 | if !ok { 79 | return conn, fmt.Errorf("channel close") 80 | } 81 | return conn, nil 82 | } 83 | 84 | func (l *Listener) Close() error { 85 | if l.l != nil && l.closeFlag == false { 86 | l.closeFlag = true 87 | l.l.Close() 88 | close(l.accept) 89 | } 90 | return nil 91 | } 92 | 93 | // wrap for TCPConn 94 | type Conn struct { 95 | TcpConn net.Conn 96 | Reader *bufio.Reader 97 | buffer *bytes.Buffer 98 | closeFlag bool 99 | 100 | mutex sync.RWMutex 101 | } 102 | 103 | func NewConn(conn net.Conn) (c *Conn) { 104 | c = &Conn{ 105 | TcpConn: conn, 106 | buffer: nil, 107 | closeFlag: false, 108 | } 109 | c.Reader = bufio.NewReader(c.TcpConn) 110 | return 111 | } 112 | 113 | func ConnectServer(addr string) (c *Conn, err error) { 114 | servertAddr, err := net.ResolveTCPAddr("tcp", addr) 115 | if err != nil { 116 | return 117 | } 118 | conn, err := net.DialTCP("tcp", nil, servertAddr) 119 | if err != nil { 120 | return 121 | } 122 | c = NewConn(conn) 123 | return c, nil 124 | } 125 | 126 | func ConnectServerByHttpProxy(httpProxy string, serverAddr string) (c *Conn, err error) { 127 | var proxyUrl *url.URL 128 | if proxyUrl, err = url.Parse(httpProxy); err != nil { 129 | return 130 | } 131 | 132 | var proxyAuth string 133 | if proxyUrl.User != nil { 134 | proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(proxyUrl.User.String())) 135 | } 136 | 137 | if proxyUrl.Scheme != "http" { 138 | err = fmt.Errorf("Proxy URL scheme must be http, not [%s]", proxyUrl.Scheme) 139 | return 140 | } 141 | 142 | if c, err = ConnectServer(proxyUrl.Host); err != nil { 143 | return 144 | } 145 | 146 | req, err := http.NewRequest("CONNECT", "http://"+serverAddr, nil) 147 | if err != nil { 148 | return 149 | } 150 | if proxyAuth != "" { 151 | req.Header.Set("Proxy-Authorization", proxyAuth) 152 | } 153 | req.Header.Set("User-Agent", "Mozilla/5.0") 154 | req.Write(c.TcpConn) 155 | 156 | resp, err := http.ReadResponse(bufio.NewReader(c), req) 157 | if err != nil { 158 | return 159 | } 160 | resp.Body.Close() 161 | if resp.StatusCode != 200 { 162 | err = fmt.Errorf("ConnectServer using proxy error, StatusCode [%d]", resp.StatusCode) 163 | return 164 | } 165 | 166 | return 167 | } 168 | 169 | // if the tcpConn is different with c.TcpConn 170 | // you should call c.Close() first 171 | func (c *Conn) SetTcpConn(tcpConn net.Conn) { 172 | c.mutex.Lock() 173 | defer c.mutex.Unlock() 174 | c.TcpConn = tcpConn 175 | c.closeFlag = false 176 | c.Reader = bufio.NewReader(c.TcpConn) 177 | } 178 | 179 | func (c *Conn) GetRemoteAddr() (addr string) { 180 | return c.TcpConn.RemoteAddr().String() 181 | } 182 | 183 | func (c *Conn) GetLocalAddr() (addr string) { 184 | return c.TcpConn.LocalAddr().String() 185 | } 186 | 187 | func (c *Conn) Read(p []byte) (n int, err error) { 188 | c.mutex.RLock() 189 | if c.buffer == nil { 190 | c.mutex.RUnlock() 191 | return c.Reader.Read(p) 192 | } 193 | c.mutex.RUnlock() 194 | 195 | n, err = c.buffer.Read(p) 196 | if err == io.EOF { 197 | c.mutex.Lock() 198 | c.buffer = nil 199 | c.mutex.Unlock() 200 | var n2 int 201 | n2, err = c.Reader.Read(p[n:]) 202 | 203 | n += n2 204 | } 205 | return 206 | } 207 | 208 | func (c *Conn) ReadLine() (buff string, err error) { 209 | buff, err = c.Reader.ReadString('\n') 210 | if err != nil { 211 | // wsarecv error in windows means connection closed? 212 | if err == io.EOF || strings.Contains(err.Error(), "wsarecv") { 213 | c.mutex.Lock() 214 | c.closeFlag = true 215 | c.mutex.Unlock() 216 | } 217 | } 218 | return buff, err 219 | } 220 | 221 | func (c *Conn) Write(content []byte) (n int, err error) { 222 | n, err = c.TcpConn.Write(content) 223 | return 224 | } 225 | 226 | func (c *Conn) WriteString(content string) (err error) { 227 | _, err = c.TcpConn.Write([]byte(content)) 228 | return err 229 | } 230 | 231 | func (c *Conn) AppendReaderBuffer(content []byte) { 232 | c.mutex.Lock() 233 | defer c.mutex.Unlock() 234 | 235 | if c.buffer == nil { 236 | c.buffer = bytes.NewBuffer(make([]byte, 0, 2048)) 237 | } 238 | c.buffer.Write(content) 239 | } 240 | 241 | func (c *Conn) SetDeadline(t time.Time) error { 242 | return c.TcpConn.SetDeadline(t) 243 | } 244 | 245 | func (c *Conn) SetReadDeadline(t time.Time) error { 246 | return c.TcpConn.SetReadDeadline(t) 247 | } 248 | 249 | func (c *Conn) Close() error { 250 | c.mutex.Lock() 251 | defer c.mutex.Unlock() 252 | if c.TcpConn != nil && c.closeFlag == false { 253 | c.closeFlag = true 254 | c.TcpConn.Close() 255 | } 256 | return nil 257 | } 258 | 259 | func (c *Conn) IsClosed() (closeFlag bool) { 260 | c.mutex.RLock() 261 | defer c.mutex.RUnlock() 262 | closeFlag = c.closeFlag 263 | return 264 | } 265 | 266 | // when you call this function, you should make sure that 267 | // no bytes were read before 268 | func (c *Conn) CheckClosed() bool { 269 | c.mutex.RLock() 270 | if c.closeFlag { 271 | c.mutex.RUnlock() 272 | return true 273 | } 274 | c.mutex.RUnlock() 275 | 276 | tmp := pool.GetBuf(2048) 277 | defer pool.PutBuf(tmp) 278 | err := c.TcpConn.SetReadDeadline(time.Now().Add(time.Millisecond)) 279 | if err != nil { 280 | c.Close() 281 | return true 282 | } 283 | 284 | n, err := c.TcpConn.Read(tmp) 285 | if err == io.EOF { 286 | return true 287 | } 288 | 289 | var tmp2 []byte = make([]byte, 1) 290 | err = c.TcpConn.SetReadDeadline(time.Now().Add(time.Millisecond)) 291 | if err != nil { 292 | c.Close() 293 | return true 294 | } 295 | 296 | n2, err := c.TcpConn.Read(tmp2) 297 | if err == io.EOF { 298 | return true 299 | } 300 | 301 | err = c.TcpConn.SetReadDeadline(time.Time{}) 302 | if err != nil { 303 | c.Close() 304 | return true 305 | } 306 | 307 | if n > 0 { 308 | c.AppendReaderBuffer(tmp[:n]) 309 | } 310 | if n2 > 0 { 311 | c.AppendReaderBuffer(tmp2[:n2]) 312 | } 313 | return false 314 | } 315 | -------------------------------------------------------------------------------- /src/models/client/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package client 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "strconv" 21 | "strings" 22 | 23 | ini "github.com/vaughan0/go-ini" 24 | ) 25 | 26 | // common config 27 | var ( 28 | ServerAddr string = "0.0.0.0" 29 | ServerPort int64 = 7000 30 | HttpProxy string = "" 31 | LogFile string = "console" 32 | LogWay string = "console" 33 | LogLevel string = "info" 34 | LogMaxDays int64 = 3 35 | PrivilegeToken string = "" 36 | HeartBeatInterval int64 = 10 37 | HeartBeatTimeout int64 = 30 38 | ) 39 | 40 | var ProxyClients map[string]*ProxyClient = make(map[string]*ProxyClient) 41 | 42 | func LoadConf(confFile string) (err error) { 43 | var tmpStr string 44 | var ok bool 45 | 46 | conf, err := ini.LoadFile(confFile) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // common 52 | tmpStr, ok = conf.Get("common", "server_addr") 53 | if ok { 54 | ServerAddr = tmpStr 55 | } 56 | 57 | tmpStr, ok = conf.Get("common", "server_port") 58 | if ok { 59 | ServerPort, _ = strconv.ParseInt(tmpStr, 10, 64) 60 | } 61 | 62 | tmpStr, ok = conf.Get("common", "http_proxy") 63 | if ok { 64 | HttpProxy = tmpStr 65 | } else { 66 | // get http_proxy from env 67 | HttpProxy = os.Getenv("http_proxy") 68 | } 69 | 70 | tmpStr, ok = conf.Get("common", "log_file") 71 | if ok { 72 | LogFile = tmpStr 73 | if LogFile == "console" { 74 | LogWay = "console" 75 | } else { 76 | LogWay = "file" 77 | } 78 | } 79 | 80 | tmpStr, ok = conf.Get("common", "log_level") 81 | if ok { 82 | LogLevel = tmpStr 83 | } 84 | 85 | tmpStr, ok = conf.Get("common", "log_max_days") 86 | if ok { 87 | LogMaxDays, _ = strconv.ParseInt(tmpStr, 10, 64) 88 | } 89 | 90 | tmpStr, ok = conf.Get("common", "privilege_token") 91 | if ok { 92 | PrivilegeToken = tmpStr 93 | } 94 | 95 | var authToken string 96 | tmpStr, ok = conf.Get("common", "auth_token") 97 | if ok { 98 | authToken = tmpStr 99 | } 100 | 101 | tmpStr, ok = conf.Get("common", "heartbeat_timeout") 102 | if ok { 103 | v, err := strconv.ParseInt(tmpStr, 10, 64) 104 | if err != nil { 105 | return fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect") 106 | } else { 107 | HeartBeatTimeout = v 108 | } 109 | } 110 | 111 | tmpStr, ok = conf.Get("common", "heartbeat_interval") 112 | if ok { 113 | v, err := strconv.ParseInt(tmpStr, 10, 64) 114 | if err != nil { 115 | return fmt.Errorf("Parse conf error: heartbeat_interval is incorrect") 116 | } else { 117 | HeartBeatInterval = v 118 | } 119 | } 120 | 121 | if HeartBeatInterval <= 0 { 122 | return fmt.Errorf("Parse conf error: heartbeat_interval is incorrect") 123 | } 124 | 125 | if HeartBeatTimeout < HeartBeatInterval { 126 | return fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect, heartbeat_timeout is less than heartbeat_interval") 127 | } 128 | 129 | // proxies 130 | for name, section := range conf { 131 | if name != "common" { 132 | proxyClient := &ProxyClient{} 133 | // name 134 | proxyClient.Name = name 135 | 136 | // auth_token 137 | proxyClient.AuthToken = authToken 138 | 139 | // local_ip 140 | proxyClient.LocalIp, ok = section["local_ip"] 141 | if !ok { 142 | // use 127.0.0.1 as default 143 | proxyClient.LocalIp = "127.0.0.1" 144 | } 145 | 146 | // local_port 147 | tmpStr, ok = section["local_port"] 148 | if ok { 149 | proxyClient.LocalPort, err = strconv.ParseInt(tmpStr, 10, 64) 150 | if err != nil { 151 | return fmt.Errorf("Parse conf error: proxy [%s] local_port error", proxyClient.Name) 152 | } 153 | } else { 154 | return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", proxyClient.Name) 155 | } 156 | 157 | // type 158 | proxyClient.Type = "tcp" 159 | tmpStr, ok = section["type"] 160 | if ok { 161 | if tmpStr != "tcp" && tmpStr != "http" && tmpStr != "https" && tmpStr != "udp" { 162 | return fmt.Errorf("Parse conf error: proxy [%s] type error", proxyClient.Name) 163 | } 164 | proxyClient.Type = tmpStr 165 | } 166 | 167 | // use_encryption 168 | proxyClient.UseEncryption = false 169 | tmpStr, ok = section["use_encryption"] 170 | if ok && tmpStr == "true" { 171 | proxyClient.UseEncryption = true 172 | } 173 | 174 | // use_gzip 175 | proxyClient.UseGzip = false 176 | tmpStr, ok = section["use_gzip"] 177 | if ok && tmpStr == "true" { 178 | proxyClient.UseGzip = true 179 | } 180 | 181 | if proxyClient.Type == "http" { 182 | // host_header_rewrite 183 | tmpStr, ok = section["host_header_rewrite"] 184 | if ok { 185 | proxyClient.HostHeaderRewrite = tmpStr 186 | } 187 | // http_user 188 | tmpStr, ok = section["http_user"] 189 | if ok { 190 | proxyClient.HttpUserName = tmpStr 191 | } 192 | // http_pwd 193 | tmpStr, ok = section["http_pwd"] 194 | if ok { 195 | proxyClient.HttpPassWord = tmpStr 196 | } 197 | 198 | } 199 | if proxyClient.Type == "http" || proxyClient.Type == "https" { 200 | // subdomain 201 | tmpStr, ok = section["subdomain"] 202 | if ok { 203 | proxyClient.SubDomain = tmpStr 204 | } 205 | } 206 | 207 | // privilege_mode 208 | proxyClient.PrivilegeMode = false 209 | tmpStr, ok = section["privilege_mode"] 210 | if ok && tmpStr == "true" { 211 | proxyClient.PrivilegeMode = true 212 | } 213 | 214 | // pool_count 215 | proxyClient.PoolCount = 0 216 | tmpStr, ok = section["pool_count"] 217 | if ok { 218 | tmpInt, err := strconv.ParseInt(tmpStr, 10, 64) 219 | if err != nil || tmpInt < 0 { 220 | return fmt.Errorf("Parse conf error: proxy [%s] pool_count error", proxyClient.Name) 221 | } 222 | proxyClient.PoolCount = tmpInt 223 | } 224 | 225 | // configures used in privilege mode 226 | if proxyClient.PrivilegeMode == true { 227 | if PrivilegeToken == "" { 228 | return fmt.Errorf("Parse conf error: proxy [%s] privilege_token must be set when privilege_mode = true", proxyClient.Name) 229 | } else { 230 | proxyClient.PrivilegeToken = PrivilegeToken 231 | } 232 | 233 | if proxyClient.Type == "tcp" || proxyClient.Type == "udp" { 234 | // remote_port 235 | tmpStr, ok = section["remote_port"] 236 | if ok { 237 | proxyClient.RemotePort, err = strconv.ParseInt(tmpStr, 10, 64) 238 | if err != nil { 239 | return fmt.Errorf("Parse conf error: proxy [%s] remote_port error", proxyClient.Name) 240 | } 241 | } else { 242 | return fmt.Errorf("Parse conf error: proxy [%s] remote_port not found", proxyClient.Name) 243 | } 244 | } else if proxyClient.Type == "http" { 245 | // custom_domains 246 | tmpStr, ok = section["custom_domains"] 247 | if ok { 248 | proxyClient.CustomDomains = strings.Split(tmpStr, ",") 249 | for i, domain := range proxyClient.CustomDomains { 250 | proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) 251 | } 252 | } 253 | 254 | // subdomain 255 | tmpStr, ok = section["subdomain"] 256 | if ok { 257 | proxyClient.SubDomain = tmpStr 258 | } 259 | 260 | if len(proxyClient.CustomDomains) == 0 && proxyClient.SubDomain == "" { 261 | return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them when type is http", proxyClient.Name) 262 | } 263 | 264 | // locations 265 | tmpStr, ok = section["locations"] 266 | if ok { 267 | proxyClient.Locations = strings.Split(tmpStr, ",") 268 | } else { 269 | proxyClient.Locations = []string{""} 270 | } 271 | } else if proxyClient.Type == "https" { 272 | // custom_domains 273 | tmpStr, ok = section["custom_domains"] 274 | if ok { 275 | proxyClient.CustomDomains = strings.Split(tmpStr, ",") 276 | for i, domain := range proxyClient.CustomDomains { 277 | proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) 278 | } 279 | } 280 | 281 | // subdomain 282 | tmpStr, ok = section["subdomain"] 283 | if ok { 284 | proxyClient.SubDomain = tmpStr 285 | } 286 | 287 | if len(proxyClient.CustomDomains) == 0 && proxyClient.SubDomain == "" { 288 | return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them when type is https", proxyClient.Name) 289 | } 290 | } 291 | } 292 | 293 | ProxyClients[proxyClient.Name] = proxyClient 294 | } 295 | } 296 | 297 | if len(ProxyClients) == 0 { 298 | return fmt.Errorf("Parse conf error: no proxy config found") 299 | } 300 | 301 | return nil 302 | } 303 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Package spew implements a deep pretty printer for Go data structures to aid in 19 | debugging. 20 | 21 | A quick overview of the additional features spew provides over the built-in 22 | printing facilities for Go data types are as follows: 23 | 24 | * Pointers are dereferenced and followed 25 | * Circular data structures are detected and handled properly 26 | * Custom Stringer/error interfaces are optionally invoked, including 27 | on unexported types 28 | * Custom types which only implement the Stringer/error interfaces via 29 | a pointer receiver are optionally invoked when passing non-pointer 30 | variables 31 | * Byte arrays and slices are dumped like the hexdump -C command which 32 | includes offsets, byte values in hex, and ASCII output (only when using 33 | Dump style) 34 | 35 | There are two different approaches spew allows for dumping Go data structures: 36 | 37 | * Dump style which prints with newlines, customizable indentation, 38 | and additional debug information such as types and all pointer addresses 39 | used to indirect to the final value 40 | * A custom Formatter interface that integrates cleanly with the standard fmt 41 | package and replaces %v, %+v, %#v, and %#+v to provide inline printing 42 | similar to the default %v while providing the additional functionality 43 | outlined above and passing unsupported format verbs such as %x and %q 44 | along to fmt 45 | 46 | Quick Start 47 | 48 | This section demonstrates how to quickly get started with spew. See the 49 | sections below for further details on formatting and configuration options. 50 | 51 | To dump a variable with full newlines, indentation, type, and pointer 52 | information use Dump, Fdump, or Sdump: 53 | spew.Dump(myVar1, myVar2, ...) 54 | spew.Fdump(someWriter, myVar1, myVar2, ...) 55 | str := spew.Sdump(myVar1, myVar2, ...) 56 | 57 | Alternatively, if you would prefer to use format strings with a compacted inline 58 | printing style, use the convenience wrappers Printf, Fprintf, etc with 59 | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or 60 | %#+v (adds types and pointer addresses): 61 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 62 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 63 | spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 64 | spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 65 | 66 | Configuration Options 67 | 68 | Configuration of spew is handled by fields in the ConfigState type. For 69 | convenience, all of the top-level functions use a global state available 70 | via the spew.Config global. 71 | 72 | It is also possible to create a ConfigState instance that provides methods 73 | equivalent to the top-level functions. This allows concurrent configuration 74 | options. See the ConfigState documentation for more details. 75 | 76 | The following configuration options are available: 77 | * Indent 78 | String to use for each indentation level for Dump functions. 79 | It is a single space by default. A popular alternative is "\t". 80 | 81 | * MaxDepth 82 | Maximum number of levels to descend into nested data structures. 83 | There is no limit by default. 84 | 85 | * DisableMethods 86 | Disables invocation of error and Stringer interface methods. 87 | Method invocation is enabled by default. 88 | 89 | * DisablePointerMethods 90 | Disables invocation of error and Stringer interface methods on types 91 | which only accept pointer receivers from non-pointer variables. 92 | Pointer method invocation is enabled by default. 93 | 94 | * DisablePointerAddresses 95 | DisablePointerAddresses specifies whether to disable the printing of 96 | pointer addresses. This is useful when diffing data structures in tests. 97 | 98 | * DisableCapacities 99 | DisableCapacities specifies whether to disable the printing of 100 | capacities for arrays, slices, maps and channels. This is useful when 101 | diffing data structures in tests. 102 | 103 | * ContinueOnMethod 104 | Enables recursion into types after invoking error and Stringer interface 105 | methods. Recursion after method invocation is disabled by default. 106 | 107 | * SortKeys 108 | Specifies map keys should be sorted before being printed. Use 109 | this to have a more deterministic, diffable output. Note that 110 | only native types (bool, int, uint, floats, uintptr and string) 111 | and types which implement error or Stringer interfaces are 112 | supported with other types sorted according to the 113 | reflect.Value.String() output which guarantees display 114 | stability. Natural map order is used by default. 115 | 116 | * SpewKeys 117 | Specifies that, as a last resort attempt, map keys should be 118 | spewed to strings and sorted by those strings. This is only 119 | considered if SortKeys is true. 120 | 121 | Dump Usage 122 | 123 | Simply call spew.Dump with a list of variables you want to dump: 124 | 125 | spew.Dump(myVar1, myVar2, ...) 126 | 127 | You may also call spew.Fdump if you would prefer to output to an arbitrary 128 | io.Writer. For example, to dump to standard error: 129 | 130 | spew.Fdump(os.Stderr, myVar1, myVar2, ...) 131 | 132 | A third option is to call spew.Sdump to get the formatted output as a string: 133 | 134 | str := spew.Sdump(myVar1, myVar2, ...) 135 | 136 | Sample Dump Output 137 | 138 | See the Dump example for details on the setup of the types and variables being 139 | shown here. 140 | 141 | (main.Foo) { 142 | unexportedField: (*main.Bar)(0xf84002e210)({ 143 | flag: (main.Flag) flagTwo, 144 | data: (uintptr) 145 | }), 146 | ExportedField: (map[interface {}]interface {}) (len=1) { 147 | (string) (len=3) "one": (bool) true 148 | } 149 | } 150 | 151 | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C 152 | command as shown. 153 | ([]uint8) (len=32 cap=32) { 154 | 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 155 | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 156 | 00000020 31 32 |12| 157 | } 158 | 159 | Custom Formatter 160 | 161 | Spew provides a custom formatter that implements the fmt.Formatter interface 162 | so that it integrates cleanly with standard fmt package printing functions. The 163 | formatter is useful for inline printing of smaller data types similar to the 164 | standard %v format specifier. 165 | 166 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 167 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 168 | combinations. Any other verbs such as %x and %q will be sent to the the 169 | standard fmt package for formatting. In addition, the custom formatter ignores 170 | the width and precision arguments (however they will still work on the format 171 | specifiers not handled by the custom formatter). 172 | 173 | Custom Formatter Usage 174 | 175 | The simplest way to make use of the spew custom formatter is to call one of the 176 | convenience functions such as spew.Printf, spew.Println, or spew.Printf. The 177 | functions have syntax you are most likely already familiar with: 178 | 179 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 180 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 181 | spew.Println(myVar, myVar2) 182 | spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 183 | spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 184 | 185 | See the Index for the full list convenience functions. 186 | 187 | Sample Formatter Output 188 | 189 | Double pointer to a uint8: 190 | %v: <**>5 191 | %+v: <**>(0xf8400420d0->0xf8400420c8)5 192 | %#v: (**uint8)5 193 | %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 194 | 195 | Pointer to circular struct with a uint8 field and a pointer to itself: 196 | %v: <*>{1 <*>} 197 | %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} 198 | %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} 199 | %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} 200 | 201 | See the Printf example for details on the setup of variables being shown 202 | here. 203 | 204 | Errors 205 | 206 | Since it is possible for custom Stringer/error interfaces to panic, spew 207 | detects them and handles them internally by printing the panic information 208 | inline with the output. Since spew is intended to provide deep pretty printing 209 | capabilities on structures, it intentionally does not return any errors. 210 | */ 211 | package spew 212 | --------------------------------------------------------------------------------