mirror of https://github.com/fatedier/frp
				
				
				
			
						commit
						90b7f2080f
					
				| 
						 | 
				
			
			@ -2,8 +2,8 @@ sudo: false
 | 
			
		|||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
    - 1.10.x
 | 
			
		||||
    - 1.11.x
 | 
			
		||||
    - 1.12.x
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
    - make
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								README.md
								
								
								
								
							
							
						
						
									
										30
									
								
								README.md
								
								
								
								
							| 
						 | 
				
			
			@ -28,8 +28,10 @@ Now it also try to support p2p connect.
 | 
			
		|||
    * [Configuration File](#configuration-file)
 | 
			
		||||
    * [Configuration file template](#configuration-file-template)
 | 
			
		||||
    * [Dashboard](#dashboard)
 | 
			
		||||
    * [Admin UI](#admin-ui)
 | 
			
		||||
    * [Authentication](#authentication)
 | 
			
		||||
    * [Encryption and Compression](#encryption-and-compression)
 | 
			
		||||
        * [TLS](#tls)
 | 
			
		||||
    * [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
 | 
			
		||||
    * [Get proxy status from client](#get-proxy-status-from-client)
 | 
			
		||||
    * [Port White List](#port-white-list)
 | 
			
		||||
| 
						 | 
				
			
			@ -389,6 +391,22 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
 | 
			
		|||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Admin UI
 | 
			
		||||
 | 
			
		||||
Admin UI help you check and manage frpc's configure.
 | 
			
		||||
 | 
			
		||||
Configure a address for admin UI to enable this feature:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
[common]
 | 
			
		||||
admin_addr = 127.0.0.1
 | 
			
		||||
admin_port = 7400
 | 
			
		||||
admin_user = admin
 | 
			
		||||
admin_pwd = admin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then visit `http://127.0.0.1:7400` to see admin UI, default username and password are both `admin`.
 | 
			
		||||
 | 
			
		||||
### Authentication
 | 
			
		||||
 | 
			
		||||
`token` in frps.ini and frpc.ini should be same.
 | 
			
		||||
| 
						 | 
				
			
			@ -407,6 +425,14 @@ use_encryption = true
 | 
			
		|||
use_compression = true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### TLS
 | 
			
		||||
 | 
			
		||||
frp support TLS protocol between frpc and frps since v0.25.0.
 | 
			
		||||
 | 
			
		||||
Config `tls_enable = true` in `common` section to frpc.ini to enable this feature.
 | 
			
		||||
 | 
			
		||||
For port multiplexing, frp send a first byte 0x17 to dial a TLS connection.
 | 
			
		||||
 | 
			
		||||
### Hot-Reload frpc configuration
 | 
			
		||||
 | 
			
		||||
First you need to set admin port in frpc's configure file to let it provide HTTP API for more features.
 | 
			
		||||
| 
						 | 
				
			
			@ -592,7 +618,7 @@ custom_domains = test.yourdomain.com
 | 
			
		|||
host_header_rewrite = dev.yourdomain.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If `host_header_rewrite` is specified, the host header will be rewritten to match the hostname portion of the forwarding address.
 | 
			
		||||
The `Host` request header will be rewritten to `Host: dev.yourdomain.com` before it reach your local http server.
 | 
			
		||||
 | 
			
		||||
### Set Headers In HTTP Request
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -736,8 +762,6 @@ plugin_http_passwd = abc
 | 
			
		|||
## Development Plan
 | 
			
		||||
 | 
			
		||||
* Log http request information in frps.
 | 
			
		||||
* Direct reverse proxy, like haproxy.
 | 
			
		||||
* kubernetes ingress support.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										29
									
								
								README_zh.md
								
								
								
								
							
							
						
						
									
										29
									
								
								README_zh.md
								
								
								
								
							| 
						 | 
				
			
			@ -24,8 +24,10 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
 | 
			
		|||
    * [配置文件](#配置文件)
 | 
			
		||||
    * [配置文件模版渲染](#配置文件模版渲染)
 | 
			
		||||
    * [Dashboard](#dashboard)
 | 
			
		||||
    * [Admin UI](#admin-ui)
 | 
			
		||||
    * [身份验证](#身份验证)
 | 
			
		||||
    * [加密与压缩](#加密与压缩)
 | 
			
		||||
        * [TLS](#tls)
 | 
			
		||||
    * [客户端热加载配置文件](#客户端热加载配置文件)
 | 
			
		||||
    * [客户端查看代理状态](#客户端查看代理状态)
 | 
			
		||||
    * [端口白名单](#端口白名单)
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +49,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
 | 
			
		|||
* [开发计划](#开发计划)
 | 
			
		||||
* [为 frp 做贡献](#为-frp-做贡献)
 | 
			
		||||
* [捐助](#捐助)
 | 
			
		||||
    * [知识星球](#知识星球)
 | 
			
		||||
    * [支付宝扫码捐赠](#支付宝扫码捐赠)
 | 
			
		||||
    * [微信支付捐赠](#微信支付捐赠)
 | 
			
		||||
    * [Paypal 捐赠](#paypal-捐赠)
 | 
			
		||||
| 
						 | 
				
			
			@ -404,6 +407,24 @@ dashboard_pwd = admin
 | 
			
		|||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Admin UI
 | 
			
		||||
 | 
			
		||||
Admin UI 可以帮助用户通过浏览器来查询和管理客户端的 proxy 状态和配置。
 | 
			
		||||
 | 
			
		||||
需要在 frpc.ini 中指定 admin 服务使用的端口,即可开启此功能:
 | 
			
		||||
 | 
			
		||||
```ini
 | 
			
		||||
[common]
 | 
			
		||||
admin_addr = 127.0.0.1
 | 
			
		||||
admin_port = 7400
 | 
			
		||||
admin_user = admin
 | 
			
		||||
admin_pwd = admin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
打开浏览器通过 `http://127.0.0.1:7400` 访问 Admin UI,用户名密码默认为 `admin`。
 | 
			
		||||
 | 
			
		||||
如果想要在外网环境访问 Admin UI,将 7400 端口映射出去即可,但需要重视安全风险。
 | 
			
		||||
 | 
			
		||||
### 身份验证
 | 
			
		||||
 | 
			
		||||
服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
 | 
			
		||||
| 
						 | 
				
			
			@ -426,6 +447,14 @@ use_compression = true
 | 
			
		|||
 | 
			
		||||
如果传输的报文长度较长,通过设置 `use_compression = true` 对传输内容进行压缩,可以有效减小 frpc 与 frps 之间的网络流量,加快流量转发速度,但是会额外消耗一些 cpu 资源。
 | 
			
		||||
 | 
			
		||||
#### TLS
 | 
			
		||||
 | 
			
		||||
从 v0.25.0 版本开始 frpc 和 frps 之间支持通过 TLS 协议加密传输。通过在 `frpc.ini` 的 `common` 中配置 `tls_enable = true` 来启用此功能,安全性更高。
 | 
			
		||||
 | 
			
		||||
为了端口复用,frp 建立 TLS 连接的第一个字节为 0x17。
 | 
			
		||||
 | 
			
		||||
**注意: 启用此功能后除 xtcp 外,不需要再设置 use_encryption。**
 | 
			
		||||
 | 
			
		||||
### 客户端热加载配置文件
 | 
			
		||||
 | 
			
		||||
当修改了 frpc 中的代理配置,可以通过 `frpc reload` 命令来动态加载配置文件,通常会在 10 秒内完成代理的更新。
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
| 
						 | 
				
			
			@ -166,8 +167,14 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
 | 
			
		|||
		}
 | 
			
		||||
		conn = frpNet.WrapConn(stream)
 | 
			
		||||
	} else {
 | 
			
		||||
		conn, err = frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
 | 
			
		||||
			fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
 | 
			
		||||
		var tlsConfig *tls.Config
 | 
			
		||||
		if g.GlbClientCfg.TLSEnable {
 | 
			
		||||
			tlsConfig = &tls.Config{
 | 
			
		||||
				InsecureSkipVerify: true,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		conn, err = frpNet.ConnectServerByProxyWithTLS(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
 | 
			
		||||
			fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort), tlsConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctl.Warn("start new connection to server error: %v", err)
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,10 @@ import (
 | 
			
		|||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +36,7 @@ import (
 | 
			
		|||
	"github.com/fatedier/golib/errors"
 | 
			
		||||
	frpIo "github.com/fatedier/golib/io"
 | 
			
		||||
	"github.com/fatedier/golib/pool"
 | 
			
		||||
	fmux "github.com/hashicorp/yamux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Proxy defines how to handle work connections for different proxy type.
 | 
			
		||||
| 
						 | 
				
			
			@ -278,32 +282,97 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pxy.Trace("get natHoleRespMsg, sid [%s], client address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr)
 | 
			
		||||
	pxy.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
 | 
			
		||||
 | 
			
		||||
	// Send sid to visitor udp address.
 | 
			
		||||
	time.Sleep(time.Second)
 | 
			
		||||
	// Send detect message
 | 
			
		||||
	array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
 | 
			
		||||
	if len(array) <= 1 {
 | 
			
		||||
		pxy.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
 | 
			
		||||
	}
 | 
			
		||||
	laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
 | 
			
		||||
	daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.VisitorAddr)
 | 
			
		||||
	/*
 | 
			
		||||
		for i := 1000; i < 65000; i++ {
 | 
			
		||||
			pxy.sendDetectMsg(array[0], int64(i), laddr, "a")
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
	port, err := strconv.ParseInt(array[1], 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Error("resolve visitor udp address error: %v", err)
 | 
			
		||||
		pxy.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
 | 
			
		||||
	pxy.Trace("send all detect msg done")
 | 
			
		||||
 | 
			
		||||
	lConn, err := net.DialUDP("udp", laddr, daddr)
 | 
			
		||||
	msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
 | 
			
		||||
 | 
			
		||||
	// Listen for clientConn's address and wait for visitor connection
 | 
			
		||||
	lConn, err := net.ListenUDP("udp", laddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Error("dial visitor udp address error: %v", err)
 | 
			
		||||
		pxy.Error("listen on visitorConn's local adress error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	lConn.Write([]byte(natHoleRespMsg.Sid))
 | 
			
		||||
	defer lConn.Close()
 | 
			
		||||
 | 
			
		||||
	kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.VisitorAddr)
 | 
			
		||||
	lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
 | 
			
		||||
	sidBuf := pool.GetBuf(1024)
 | 
			
		||||
	var uAddr *net.UDPAddr
 | 
			
		||||
	n, uAddr, err = lConn.ReadFromUDP(sidBuf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Warn("get sid from visitor error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	lConn.SetReadDeadline(time.Time{})
 | 
			
		||||
	if string(sidBuf[:n]) != natHoleRespMsg.Sid {
 | 
			
		||||
		pxy.Warn("incorrect sid from visitor")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	pool.PutBuf(sidBuf)
 | 
			
		||||
	pxy.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
 | 
			
		||||
 | 
			
		||||
	lConn.WriteToUDP(sidBuf[:n], uAddr)
 | 
			
		||||
 | 
			
		||||
	kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, false, natHoleRespMsg.VisitorAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Error("create kcp connection from udp connection error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmuxCfg := fmux.DefaultConfig()
 | 
			
		||||
	fmuxCfg.KeepAliveInterval = 5 * time.Second
 | 
			
		||||
	fmuxCfg.LogOutput = ioutil.Discard
 | 
			
		||||
	sess, err := fmux.Server(kcpConn, fmuxCfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Error("create yamux server from kcp connection error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	muxConn, err := sess.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		pxy.Error("accept for yamux connection error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf,
 | 
			
		||||
		frpNet.WrapConn(kcpConn), []byte(pxy.cfg.Sk))
 | 
			
		||||
		frpNet.WrapConn(muxConn), []byte(pxy.cfg.Sk))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
 | 
			
		||||
	daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", addr, port))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tConn, err := net.DialUDP("udp", laddr, daddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//uConn := ipv4.NewConn(tConn)
 | 
			
		||||
	//uConn.SetTTL(3)
 | 
			
		||||
 | 
			
		||||
	tConn.Write(content)
 | 
			
		||||
	tConn.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UDP
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"runtime"
 | 
			
		||||
| 
						 | 
				
			
			@ -151,8 +152,14 @@ func (svr *Service) keepControllerWorking() {
 | 
			
		|||
// conn: control connection
 | 
			
		||||
// session: if it's not nil, using tcp mux
 | 
			
		||||
func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error) {
 | 
			
		||||
	conn, err = frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
 | 
			
		||||
		fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
 | 
			
		||||
	var tlsConfig *tls.Config
 | 
			
		||||
	if g.GlbClientCfg.TLSEnable {
 | 
			
		||||
		tlsConfig = &tls.Config{
 | 
			
		||||
			InsecureSkipVerify: true,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	conn, err = frpNet.ConnectServerByProxyWithTLS(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
 | 
			
		||||
		fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort), tlsConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,14 +18,11 @@ import (
 | 
			
		|||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/ipv4"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/g"
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"github.com/fatedier/frp/models/msg"
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +32,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	frpIo "github.com/fatedier/golib/io"
 | 
			
		||||
	"github.com/fatedier/golib/pool"
 | 
			
		||||
	fmux "github.com/hashicorp/yamux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Visitor is used for forward traffics from local port tot remote service.
 | 
			
		||||
| 
						 | 
				
			
			@ -249,40 +247,31 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sv.Trace("get natHoleRespMsg, sid [%s], client address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr)
 | 
			
		||||
	sv.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
 | 
			
		||||
 | 
			
		||||
	// Close visitorConn, so we can use it's local address.
 | 
			
		||||
	visitorConn.Close()
 | 
			
		||||
 | 
			
		||||
	// Send detect message.
 | 
			
		||||
	array := strings.Split(natHoleRespMsg.ClientAddr, ":")
 | 
			
		||||
	if len(array) <= 1 {
 | 
			
		||||
		sv.Error("get natHoleResp client address error: %s", natHoleRespMsg.ClientAddr)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// send sid message to client
 | 
			
		||||
	laddr, _ := net.ResolveUDPAddr("udp", visitorConn.LocalAddr().String())
 | 
			
		||||
	/*
 | 
			
		||||
		for i := 1000; i < 65000; i++ {
 | 
			
		||||
			sv.sendDetectMsg(array[0], int64(i), laddr, "a")
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
	port, err := strconv.ParseInt(array[1], 10, 64)
 | 
			
		||||
	daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.ClientAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("get natHoleResp client address error: %s", natHoleRespMsg.ClientAddr)
 | 
			
		||||
		sv.Error("resolve client udp address error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sv.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
 | 
			
		||||
	sv.Trace("send all detect msg done")
 | 
			
		||||
	lConn, err := net.DialUDP("udp", laddr, daddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("dial client udp address error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer lConn.Close()
 | 
			
		||||
 | 
			
		||||
	// Listen for visitorConn's address and wait for client connection.
 | 
			
		||||
	lConn, err := net.ListenUDP("udp", laddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("listen on visitorConn's local adress error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	lConn.SetReadDeadline(time.Now().Add(5 * time.Second))
 | 
			
		||||
	lConn.Write([]byte(natHoleRespMsg.Sid))
 | 
			
		||||
 | 
			
		||||
	// read ack sid from client
 | 
			
		||||
	sidBuf := pool.GetBuf(1024)
 | 
			
		||||
	n, _, err = lConn.ReadFromUDP(sidBuf)
 | 
			
		||||
	lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
 | 
			
		||||
	n, err = lConn.Read(sidBuf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Warn("get sid from client error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -292,11 +281,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 | 
			
		|||
		sv.Warn("incorrect sid from client")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sv.Info("nat hole connection make success, sid [%s]", string(sidBuf[:n]))
 | 
			
		||||
	pool.PutBuf(sidBuf)
 | 
			
		||||
 | 
			
		||||
	sv.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
 | 
			
		||||
 | 
			
		||||
	// wrap kcp connection
 | 
			
		||||
	var remote io.ReadWriteCloser
 | 
			
		||||
	remote, err = frpNet.NewKcpConnFromUdp(lConn, false, natHoleRespMsg.ClientAddr)
 | 
			
		||||
	remote, err = frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.ClientAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("create kcp connection from udp connection error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -314,25 +305,21 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 | 
			
		|||
		remote = frpIo.WithCompression(remote)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpIo.Join(userConn, remote)
 | 
			
		||||
	fmuxCfg := fmux.DefaultConfig()
 | 
			
		||||
	fmuxCfg.KeepAliveInterval = 5 * time.Second
 | 
			
		||||
	fmuxCfg.LogOutput = ioutil.Discard
 | 
			
		||||
	sess, err := fmux.Client(remote, fmuxCfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("create yamux session error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	muxConn, err := sess.Open()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sv.Error("open yamux stream error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpIo.Join(userConn, muxConn)
 | 
			
		||||
	sv.Debug("join connections closed")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sv *XtcpVisitor) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
 | 
			
		||||
	daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", addr, port))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tConn, err := net.DialUDP("udp", laddr, daddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uConn := ipv4.NewConn(tConn)
 | 
			
		||||
	uConn.SetTTL(3)
 | 
			
		||||
 | 
			
		||||
	tConn.Write(content)
 | 
			
		||||
	tConn.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ var xtcpCmd = &cobra.Command{
 | 
			
		|||
		if role == "server" {
 | 
			
		||||
			cfg := &config.XtcpProxyConf{}
 | 
			
		||||
			cfg.ProxyName = prefix + proxyName
 | 
			
		||||
			cfg.ProxyType = consts.StcpProxy
 | 
			
		||||
			cfg.ProxyType = consts.XtcpProxy
 | 
			
		||||
			cfg.UseEncryption = useEncryption
 | 
			
		||||
			cfg.UseCompression = useCompression
 | 
			
		||||
			cfg.Role = role
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ var xtcpCmd = &cobra.Command{
 | 
			
		|||
		} else if role == "visitor" {
 | 
			
		||||
			cfg := &config.XtcpVisitorConf{}
 | 
			
		||||
			cfg.ProxyName = prefix + proxyName
 | 
			
		||||
			cfg.ProxyType = consts.StcpProxy
 | 
			
		||||
			cfg.ProxyType = consts.XtcpProxy
 | 
			
		||||
			cfg.UseEncryption = useEncryption
 | 
			
		||||
			cfg.UseCompression = useCompression
 | 
			
		||||
			cfg.Role = role
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,9 @@ login_fail_exit = true
 | 
			
		|||
# now it supports tcp and kcp and websocket, default is tcp
 | 
			
		||||
protocol = tcp
 | 
			
		||||
 | 
			
		||||
# if tls_enable is true, frpc will connect frps by tls
 | 
			
		||||
tls_enable = true
 | 
			
		||||
 | 
			
		||||
# specify a dns server, so frpc will use this instead of default one
 | 
			
		||||
# dns_server = 8.8.8.8
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -12,7 +12,7 @@ require (
 | 
			
		|||
	github.com/gorilla/context v1.1.1 // indirect
 | 
			
		||||
	github.com/gorilla/mux v1.6.2
 | 
			
		||||
	github.com/gorilla/websocket v1.2.0
 | 
			
		||||
	github.com/hashicorp/yamux v0.0.0-20180314200745-2658be15c5f0
 | 
			
		||||
	github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
 | 
			
		||||
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.4 // indirect
 | 
			
		||||
	github.com/pkg/errors v0.8.0 // indirect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										3
									
								
								go.sum
								
								
								
								
							| 
						 | 
				
			
			@ -9,7 +9,8 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8
 | 
			
		|||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
			
		||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/hashicorp/yamux v0.0.0-20180314200745-2658be15c5f0/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 | 
			
		||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
 | 
			
		||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ type ClientCommonConf struct {
 | 
			
		|||
	LoginFailExit     bool                `json:"login_fail_exit"`
 | 
			
		||||
	Start             map[string]struct{} `json:"start"`
 | 
			
		||||
	Protocol          string              `json:"protocol"`
 | 
			
		||||
	TLSEnable         bool                `json:"tls_enable"`
 | 
			
		||||
	HeartBeatInterval int64               `json:"heartbeat_interval"`
 | 
			
		||||
	HeartBeatTimeout  int64               `json:"heartbeat_timeout"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +70,7 @@ func GetDefaultClientConf() *ClientCommonConf {
 | 
			
		|||
		LoginFailExit:     true,
 | 
			
		||||
		Start:             make(map[string]struct{}),
 | 
			
		||||
		Protocol:          "tcp",
 | 
			
		||||
		TLSEnable:         false,
 | 
			
		||||
		HeartBeatInterval: 30,
 | 
			
		||||
		HeartBeatTimeout:  90,
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -194,6 +196,12 @@ func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (c
 | 
			
		|||
		cfg.Protocol = tmpStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "tls_enable"); ok && tmpStr == "true" {
 | 
			
		||||
		cfg.TLSEnable = true
 | 
			
		||||
	} else {
 | 
			
		||||
		cfg.TLSEnable = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
 | 
			
		||||
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 | 
			
		||||
			err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,44 +17,46 @@ package msg
 | 
			
		|||
import "net"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TypeLogin              = 'o'
 | 
			
		||||
	TypeLoginResp          = '1'
 | 
			
		||||
	TypeNewProxy           = 'p'
 | 
			
		||||
	TypeNewProxyResp       = '2'
 | 
			
		||||
	TypeCloseProxy         = 'c'
 | 
			
		||||
	TypeNewWorkConn        = 'w'
 | 
			
		||||
	TypeReqWorkConn        = 'r'
 | 
			
		||||
	TypeStartWorkConn      = 's'
 | 
			
		||||
	TypeNewVisitorConn     = 'v'
 | 
			
		||||
	TypeNewVisitorConnResp = '3'
 | 
			
		||||
	TypePing               = 'h'
 | 
			
		||||
	TypePong               = '4'
 | 
			
		||||
	TypeUdpPacket          = 'u'
 | 
			
		||||
	TypeNatHoleVisitor     = 'i'
 | 
			
		||||
	TypeNatHoleClient      = 'n'
 | 
			
		||||
	TypeNatHoleResp        = 'm'
 | 
			
		||||
	TypeNatHoleSid         = '5'
 | 
			
		||||
	TypeLogin                 = 'o'
 | 
			
		||||
	TypeLoginResp             = '1'
 | 
			
		||||
	TypeNewProxy              = 'p'
 | 
			
		||||
	TypeNewProxyResp          = '2'
 | 
			
		||||
	TypeCloseProxy            = 'c'
 | 
			
		||||
	TypeNewWorkConn           = 'w'
 | 
			
		||||
	TypeReqWorkConn           = 'r'
 | 
			
		||||
	TypeStartWorkConn         = 's'
 | 
			
		||||
	TypeNewVisitorConn        = 'v'
 | 
			
		||||
	TypeNewVisitorConnResp    = '3'
 | 
			
		||||
	TypePing                  = 'h'
 | 
			
		||||
	TypePong                  = '4'
 | 
			
		||||
	TypeUdpPacket             = 'u'
 | 
			
		||||
	TypeNatHoleVisitor        = 'i'
 | 
			
		||||
	TypeNatHoleClient         = 'n'
 | 
			
		||||
	TypeNatHoleResp           = 'm'
 | 
			
		||||
	TypeNatHoleClientDetectOK = 'd'
 | 
			
		||||
	TypeNatHoleSid            = '5'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	msgTypeMap = map[byte]interface{}{
 | 
			
		||||
		TypeLogin:              Login{},
 | 
			
		||||
		TypeLoginResp:          LoginResp{},
 | 
			
		||||
		TypeNewProxy:           NewProxy{},
 | 
			
		||||
		TypeNewProxyResp:       NewProxyResp{},
 | 
			
		||||
		TypeCloseProxy:         CloseProxy{},
 | 
			
		||||
		TypeNewWorkConn:        NewWorkConn{},
 | 
			
		||||
		TypeReqWorkConn:        ReqWorkConn{},
 | 
			
		||||
		TypeStartWorkConn:      StartWorkConn{},
 | 
			
		||||
		TypeNewVisitorConn:     NewVisitorConn{},
 | 
			
		||||
		TypeNewVisitorConnResp: NewVisitorConnResp{},
 | 
			
		||||
		TypePing:               Ping{},
 | 
			
		||||
		TypePong:               Pong{},
 | 
			
		||||
		TypeUdpPacket:          UdpPacket{},
 | 
			
		||||
		TypeNatHoleVisitor:     NatHoleVisitor{},
 | 
			
		||||
		TypeNatHoleClient:      NatHoleClient{},
 | 
			
		||||
		TypeNatHoleResp:        NatHoleResp{},
 | 
			
		||||
		TypeNatHoleSid:         NatHoleSid{},
 | 
			
		||||
		TypeLogin:                 Login{},
 | 
			
		||||
		TypeLoginResp:             LoginResp{},
 | 
			
		||||
		TypeNewProxy:              NewProxy{},
 | 
			
		||||
		TypeNewProxyResp:          NewProxyResp{},
 | 
			
		||||
		TypeCloseProxy:            CloseProxy{},
 | 
			
		||||
		TypeNewWorkConn:           NewWorkConn{},
 | 
			
		||||
		TypeReqWorkConn:           ReqWorkConn{},
 | 
			
		||||
		TypeStartWorkConn:         StartWorkConn{},
 | 
			
		||||
		TypeNewVisitorConn:        NewVisitorConn{},
 | 
			
		||||
		TypeNewVisitorConnResp:    NewVisitorConnResp{},
 | 
			
		||||
		TypePing:                  Ping{},
 | 
			
		||||
		TypePong:                  Pong{},
 | 
			
		||||
		TypeUdpPacket:             UdpPacket{},
 | 
			
		||||
		TypeNatHoleVisitor:        NatHoleVisitor{},
 | 
			
		||||
		TypeNatHoleClient:         NatHoleClient{},
 | 
			
		||||
		TypeNatHoleResp:           NatHoleResp{},
 | 
			
		||||
		TypeNatHoleClientDetectOK: NatHoleClientDetectOK{},
 | 
			
		||||
		TypeNatHoleSid:            NatHoleSid{},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,6 +171,9 @@ type NatHoleResp struct {
 | 
			
		|||
	Error       string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NatHoleClientDetectOK struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NatHoleSid struct {
 | 
			
		||||
	Sid string `json:"sid"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,11 @@ import (
 | 
			
		|||
// Timeout seconds.
 | 
			
		||||
var NatHoleTimeout int64 = 10
 | 
			
		||||
 | 
			
		||||
type SidRequest struct {
 | 
			
		||||
	Sid      string
 | 
			
		||||
	NotifyCh chan struct{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NatHoleController struct {
 | 
			
		||||
	listener *net.UDPConn
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,11 +49,11 @@ func NewNatHoleController(udpBindAddr string) (nc *NatHoleController, err error)
 | 
			
		|||
	return nc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (nc *NatHoleController) ListenClient(name string, sk string) (sidCh chan string) {
 | 
			
		||||
func (nc *NatHoleController) ListenClient(name string, sk string) (sidCh chan *SidRequest) {
 | 
			
		||||
	clientCfg := &NatHoleClientCfg{
 | 
			
		||||
		Name:  name,
 | 
			
		||||
		Sk:    sk,
 | 
			
		||||
		SidCh: make(chan string),
 | 
			
		||||
		SidCh: make(chan *SidRequest),
 | 
			
		||||
	}
 | 
			
		||||
	nc.mu.Lock()
 | 
			
		||||
	nc.clientCfgs[name] = clientCfg
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +137,10 @@ func (nc *NatHoleController) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDP
 | 
			
		|||
	}()
 | 
			
		||||
 | 
			
		||||
	err := errors.PanicToError(func() {
 | 
			
		||||
		clientCfg.SidCh <- sid
 | 
			
		||||
		clientCfg.SidCh <- &SidRequest{
 | 
			
		||||
			Sid:      sid,
 | 
			
		||||
			NotifyCh: session.NotifyCh,
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +166,6 @@ func (nc *NatHoleController) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAd
 | 
			
		|||
	}
 | 
			
		||||
	log.Trace("handle client message, sid [%s]", session.Sid)
 | 
			
		||||
	session.ClientAddr = raddr
 | 
			
		||||
	session.NotifyCh <- struct{}{}
 | 
			
		||||
 | 
			
		||||
	resp := nc.GenNatHoleResponse(session, "")
 | 
			
		||||
	log.Trace("send nat hole response to client")
 | 
			
		||||
| 
						 | 
				
			
			@ -201,5 +208,5 @@ type NatHoleSession struct {
 | 
			
		|||
type NatHoleClientCfg struct {
 | 
			
		||||
	Name  string
 | 
			
		||||
	Sk    string
 | 
			
		||||
	SidCh chan string
 | 
			
		||||
	SidCh chan *SidRequest
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,18 +42,40 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
 | 
			
		|||
			select {
 | 
			
		||||
			case <-pxy.closeCh:
 | 
			
		||||
				break
 | 
			
		||||
			case sid := <-sidCh:
 | 
			
		||||
			case sidRequest := <-sidCh:
 | 
			
		||||
				sr := sidRequest
 | 
			
		||||
				workConn, errRet := pxy.GetWorkConnFromPool()
 | 
			
		||||
				if errRet != nil {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				m := &msg.NatHoleSid{
 | 
			
		||||
					Sid: sid,
 | 
			
		||||
					Sid: sr.Sid,
 | 
			
		||||
				}
 | 
			
		||||
				errRet = msg.WriteMsg(workConn, m)
 | 
			
		||||
				if errRet != nil {
 | 
			
		||||
					pxy.Warn("write nat hole sid package error, %v", errRet)
 | 
			
		||||
					workConn.Close()
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				go func() {
 | 
			
		||||
					raw, errRet := msg.ReadMsg(workConn)
 | 
			
		||||
					if errRet != nil {
 | 
			
		||||
						pxy.Warn("read nat hole client ok package error: %v", errRet)
 | 
			
		||||
						workConn.Close()
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok {
 | 
			
		||||
						pxy.Warn("read nat hole client ok package format error")
 | 
			
		||||
						workConn.Close()
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					select {
 | 
			
		||||
					case sr.NotifyCh <- struct{}{}:
 | 
			
		||||
					default:
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,14 @@ package server
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +67,9 @@ type Service struct {
 | 
			
		|||
	// Accept connections using websocket
 | 
			
		||||
	websocketListener frpNet.Listener
 | 
			
		||||
 | 
			
		||||
	// Accept frp tls connections
 | 
			
		||||
	tlsListener frpNet.Listener
 | 
			
		||||
 | 
			
		||||
	// Manage all controllers
 | 
			
		||||
	ctlManager *ControlManager
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +81,8 @@ type Service struct {
 | 
			
		|||
 | 
			
		||||
	// stats collector to store server and proxies stats info
 | 
			
		||||
	statsCollector stats.Collector
 | 
			
		||||
 | 
			
		||||
	tlsConfig *tls.Config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewService() (svr *Service, err error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +95,7 @@ func NewService() (svr *Service, err error) {
 | 
			
		|||
			TcpPortManager: ports.NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
 | 
			
		||||
			UdpPortManager: ports.NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
 | 
			
		||||
		},
 | 
			
		||||
		tlsConfig: generateTLSConfig(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Init group controller
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +199,12 @@ func NewService() (svr *Service, err error) {
 | 
			
		|||
		log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// frp tls listener
 | 
			
		||||
	tlsListener := svr.muxer.Listen(1, 1, func(data []byte) bool {
 | 
			
		||||
		return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
 | 
			
		||||
	})
 | 
			
		||||
	svr.tlsListener = frpNet.WrapLogListener(tlsListener)
 | 
			
		||||
 | 
			
		||||
	// Create nat hole controller.
 | 
			
		||||
	if cfg.BindUdpPort > 0 {
 | 
			
		||||
		var nc *nathole.NatHoleController
 | 
			
		||||
| 
						 | 
				
			
			@ -225,6 +243,7 @@ func (svr *Service) Run() {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	go svr.HandleListener(svr.websocketListener)
 | 
			
		||||
	go svr.HandleListener(svr.tlsListener)
 | 
			
		||||
 | 
			
		||||
	svr.HandleListener(svr.listener)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -237,6 +256,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 | 
			
		|||
			log.Warn("Listener for incoming connections from client closed")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c = frpNet.CheckAndEnableTLSServerConn(c, svr.tlsConfig)
 | 
			
		||||
 | 
			
		||||
		// Start a new goroutine for dealing connections.
 | 
			
		||||
		go func(frpConn frpNet.Conn) {
 | 
			
		||||
| 
						 | 
				
			
			@ -373,3 +393,24 @@ func (svr *Service) RegisterVisitorConn(visitorConn frpNet.Conn, newMsg *msg.New
 | 
			
		|||
	return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
 | 
			
		||||
		newMsg.UseEncryption, newMsg.UseCompression)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Setup a bare-bones TLS config for the server
 | 
			
		||||
func generateTLSConfig() *tls.Config {
 | 
			
		||||
	key, err := rsa.GenerateKey(rand.Reader, 1024)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	template := x509.Certificate{SerialNumber: big.NewInt(1)}
 | 
			
		||||
	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
 | 
			
		||||
	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
 | 
			
		||||
 | 
			
		||||
	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return &tls.Config{Certificates: []tls.Certificate{tlsCert}}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ func TestCmdTcp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer s.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"tcp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
 | 
			
		||||
		"-l", "10701", "-r", "20801", "-n", "tcp_test"})
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ func TestCmdTcp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer c.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ func TestCmdUdp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer s.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"udp", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
 | 
			
		||||
		"-l", "10702", "-r", "20802", "-n", "udp_test"})
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ func TestCmdUdp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer c.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	res, err := util.SendUdpMsg("127.0.0.1:20802", consts.TEST_UDP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ func TestCmdHttp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer s.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	c := util.NewProcess(consts.FRPC_BIN_PATH, []string{"http", "-s", "127.0.0.1:20000", "-t", "123", "-u", "test",
 | 
			
		||||
		"-n", "udp_test", "-l", "10704", "--custom_domain", "127.0.0.1"})
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ func TestCmdHttp(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer c.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	code, body, _, err := util.SendHttpMsg("GET", "http://127.0.0.1:20001", "", nil, "")
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,14 +56,14 @@ func TestReconnect(t *testing.T) {
 | 
			
		|||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ func TestReconnect(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	// stop frpc
 | 
			
		||||
	frpcProcess.Stop()
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp, expect failed
 | 
			
		||||
	_, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ func TestReconnect(t *testing.T) {
 | 
			
		|||
	if assert.NoError(err) {
 | 
			
		||||
		defer newFrpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ func TestReconnect(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	// stop frps
 | 
			
		||||
	frpsProcess.Stop()
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp, expect failed
 | 
			
		||||
	_, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,7 +94,7 @@ func TestReload(t *testing.T) {
 | 
			
		|||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ func TestReload(t *testing.T) {
 | 
			
		|||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp1
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ func TestConfTemplate(t *testing.T) {
 | 
			
		|||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(100 * time.Millisecond)
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess("env", []string{"FRP_TOKEN=123456", "TCP_REMOTE_PORT=20801", consts.FRPC_BIN_PATH, "-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ func TestConfTemplate(t *testing.T) {
 | 
			
		|||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(250 * time.Millisecond)
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp1
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,188 @@
 | 
			
		|||
package ci
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/fatedier/frp/tests/config"
 | 
			
		||||
	"github.com/fatedier/frp/tests/consts"
 | 
			
		||||
	"github.com/fatedier/frp/tests/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const FRPS_TLS_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TLS_TCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = tcp
 | 
			
		||||
tls_enable = true
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20801
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestTlsOverTCP(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
	frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TLS_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_TCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
 | 
			
		||||
	err = frpsProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FRPS_TLS_KCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 20000
 | 
			
		||||
kcp_bind_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TLS_KCP_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = kcp
 | 
			
		||||
tls_enable = true
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20801
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestTLSOverKCP(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
	frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TLS_KCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_KCP_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
 | 
			
		||||
	err = frpsProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FRPS_TLS_WS_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
bind_addr = 0.0.0.0
 | 
			
		||||
bind_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const FRPC_TLS_WS_CONF = `
 | 
			
		||||
[common]
 | 
			
		||||
server_addr = 127.0.0.1
 | 
			
		||||
server_port = 20000
 | 
			
		||||
log_file = console
 | 
			
		||||
log_level = debug
 | 
			
		||||
token = 123456
 | 
			
		||||
protocol = websocket
 | 
			
		||||
tls_enable = true
 | 
			
		||||
 | 
			
		||||
[tcp]
 | 
			
		||||
type = tcp
 | 
			
		||||
local_port = 10701
 | 
			
		||||
remote_port = 20801
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestTLSOverWebsocket(t *testing.T) {
 | 
			
		||||
	assert := assert.New(t)
 | 
			
		||||
	frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TLS_WS_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpsCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_WS_CONF)
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer os.Remove(frpcCfgPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frpsProcess := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", frpsCfgPath})
 | 
			
		||||
	err = frpsProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpsProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(200 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	frpcProcess := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcCfgPath})
 | 
			
		||||
	err = frpcProcess.Start()
 | 
			
		||||
	if assert.NoError(err) {
 | 
			
		||||
		defer frpcProcess.Stop()
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(500 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	// test tcp
 | 
			
		||||
	res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR)
 | 
			
		||||
	assert.NoError(err)
 | 
			
		||||
	assert.Equal(consts.TEST_TCP_ECHO_STR, res)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
package net
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
| 
						 | 
				
			
			@ -207,3 +208,13 @@ func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn
 | 
			
		|||
		return nil, fmt.Errorf("unsupport protocol: %s", protocol)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c Conn, err error) {
 | 
			
		||||
	c, err = ConnectServerByProxy(proxyUrl, protocol, addr)
 | 
			
		||||
	if tlsConfig == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c = WrapTLSClientConn(c, tlsConfig)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
// Copyright 2019 fatedier, fatedier@gmail.com
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package net
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	gnet "github.com/fatedier/golib/net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	FRP_TLS_HEAD_BYTE = 0x17
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out Conn) {
 | 
			
		||||
	c.Write([]byte{byte(FRP_TLS_HEAD_BYTE)})
 | 
			
		||||
	out = WrapConn(tls.Client(c, tlsConfig))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CheckAndEnableTLSServerConn(c net.Conn, tlsConfig *tls.Config) (out Conn) {
 | 
			
		||||
	sc, r := gnet.NewSharedConnSize(c, 1)
 | 
			
		||||
	buf := make([]byte, 1)
 | 
			
		||||
	n, _ := r.Read(buf)
 | 
			
		||||
	if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE {
 | 
			
		||||
		out = WrapConn(tls.Server(c, tlsConfig))
 | 
			
		||||
	} else {
 | 
			
		||||
		out = WrapConn(sc)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var version string = "0.24.1"
 | 
			
		||||
var version string = "0.25.0"
 | 
			
		||||
 | 
			
		||||
func Full() string {
 | 
			
		||||
	return version
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
module github.com/hashicorp/yamux
 | 
			
		||||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package yamux
 | 
			
		|||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +31,13 @@ type Config struct {
 | 
			
		|||
	// window size that we allow for a stream.
 | 
			
		||||
	MaxStreamWindowSize uint32
 | 
			
		||||
 | 
			
		||||
	// LogOutput is used to control the log destination
 | 
			
		||||
	// LogOutput is used to control the log destination. Either Logger or
 | 
			
		||||
	// LogOutput can be set, not both.
 | 
			
		||||
	LogOutput io.Writer
 | 
			
		||||
 | 
			
		||||
	// Logger is used to pass in the logger to be used. Either Logger or
 | 
			
		||||
	// LogOutput can be set, not both.
 | 
			
		||||
	Logger *log.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultConfig is used to return a default configuration
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +63,11 @@ func VerifyConfig(config *Config) error {
 | 
			
		|||
	if config.MaxStreamWindowSize < initialStreamWindow {
 | 
			
		||||
		return fmt.Errorf("MaxStreamWindowSize must be larger than %d", initialStreamWindow)
 | 
			
		||||
	}
 | 
			
		||||
	if config.LogOutput != nil && config.Logger != nil {
 | 
			
		||||
		return fmt.Errorf("both Logger and LogOutput may not be set, select one")
 | 
			
		||||
	} else if config.LogOutput == nil && config.Logger == nil {
 | 
			
		||||
		return fmt.Errorf("one of Logger or LogOutput must be set, select one")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,9 +86,14 @@ type sendReady struct {
 | 
			
		|||
 | 
			
		||||
// newSession is used to construct a new session
 | 
			
		||||
func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
 | 
			
		||||
	logger := config.Logger
 | 
			
		||||
	if logger == nil {
 | 
			
		||||
		logger = log.New(config.LogOutput, "", log.LstdFlags)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := &Session{
 | 
			
		||||
		config:     config,
 | 
			
		||||
		logger:     log.New(config.LogOutput, "", log.LstdFlags),
 | 
			
		||||
		logger:     logger,
 | 
			
		||||
		conn:       conn,
 | 
			
		||||
		bufRead:    bufio.NewReader(conn),
 | 
			
		||||
		pings:      make(map[uint32]chan struct{}),
 | 
			
		||||
| 
						 | 
				
			
			@ -309,8 +314,10 @@ func (s *Session) keepalive() {
 | 
			
		|||
		case <-time.After(s.config.KeepAliveInterval):
 | 
			
		||||
			_, err := s.Ping()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				s.logger.Printf("[ERR] yamux: keepalive failed: %v", err)
 | 
			
		||||
				s.exitErr(ErrKeepAliveTimeout)
 | 
			
		||||
				if err != ErrSessionShutdown {
 | 
			
		||||
					s.logger.Printf("[ERR] yamux: keepalive failed: %v", err)
 | 
			
		||||
					s.exitErr(ErrKeepAliveTimeout)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		case <-s.shutdownCh:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ github.com/gorilla/context
 | 
			
		|||
github.com/gorilla/mux
 | 
			
		||||
# github.com/gorilla/websocket v1.2.0
 | 
			
		||||
github.com/gorilla/websocket
 | 
			
		||||
# github.com/hashicorp/yamux v0.0.0-20180314200745-2658be15c5f0
 | 
			
		||||
# github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
 | 
			
		||||
github.com/hashicorp/yamux
 | 
			
		||||
# github.com/inconshreveable/mousetrap v1.0.0
 | 
			
		||||
github.com/inconshreveable/mousetrap
 | 
			
		||||
| 
						 | 
				
			
			@ -61,11 +61,11 @@ golang.org/x/crypto/twofish
 | 
			
		|||
golang.org/x/crypto/xtea
 | 
			
		||||
golang.org/x/crypto/salsa20/salsa
 | 
			
		||||
# golang.org/x/net v0.0.0-20180524181706-dfa909b99c79
 | 
			
		||||
golang.org/x/net/ipv4
 | 
			
		||||
golang.org/x/net/websocket
 | 
			
		||||
golang.org/x/net/context
 | 
			
		||||
golang.org/x/net/proxy
 | 
			
		||||
golang.org/x/net/ipv4
 | 
			
		||||
golang.org/x/net/internal/socks
 | 
			
		||||
golang.org/x/net/bpf
 | 
			
		||||
golang.org/x/net/internal/iana
 | 
			
		||||
golang.org/x/net/internal/socket
 | 
			
		||||
golang.org/x/net/context
 | 
			
		||||
golang.org/x/net/proxy
 | 
			
		||||
golang.org/x/net/internal/socks
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue