frp改版-支持域前置
字数 917 2025-08-27 12:33:37
FRP改版支持域前置与WSS实现教学文档
1. 背景介绍
FRP是一个高性能的反向代理应用,可以帮助用户轻松地进行内网穿透。本教程将详细介绍如何修改FRP以支持域前置(domain fronting)和WebSocket Secure(WSS)协议。
2. 核心修改点
2.1 WSS协议支持
FRP原生不支持WSS协议,需要以下修改:
- CDN配置:需要将CDN回源协议配置为HTTP
- 错误处理:直接使用WSS会导致FRP服务端报错"invalid protocol version"
2.2 代码修改位置
2.2.1 WebSocket Host配置修改
文件路径:golang.org/x/net/websocket/hybi.go
修改hybiClientHandshake函数:
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
host := config.Location.Host
if tmpHost := config.Header.Get("Host"); tmpHost != "" {
host = tmpHost
}
bw.WriteString("Host: " + removeZone(host) + "\r\n")
// ...其他代码不变...
}
2.2.2 NewConfig函数修改
文件路径:golang.org/x/net/websocket/client.go
func NewConfig(server, origin string, websocket_domain string) (config *Config, err error) {
config = new(Config)
config.Version = ProtocolVersionHybi13
config.Location, err = url.ParseRequestURI(server)
if err != nil {
return
}
config.Origin, err = url.ParseRequestURI(origin)
if err != nil {
return
}
config.Header = http.Header(make(map[string][]string))
config.Header.Set("Host", websocket_domain)
return
}
2.2.3 ConnectWebsocketServer函数修改
文件路径:util/net/websocket.go
func ConnectWebsocketServer(addr string, websocket_domain string, isSecure bool) (net.Conn, error) {
if isSecure {
ho := strings.Split(addr, ":")
ip, err := net.ResolveIPAddr("ip", ho[0])
ip_addr := ip.String() + ":" + ho[1]
if err != nil {
return nil, err
}
addr = "wss://" + ip_addr + FrpWebsocketPath
} else {
addr = "ws://" + addr + FrpWebsocketPath
}
uri, err := url.Parse(addr)
if err != nil {
return nil, err
}
var origin string
if isSecure {
ho := strings.Split(uri.Host, ":")
ip, err := net.ResolveIPAddr("ip", ho[0])
ip_addr := ip.String() + ":" + ho[1]
if err != nil {
return nil, err
}
origin = "https://" + ip_addr
} else {
origin = "http://" + uri.Host
}
fmt.Println("addr:" + addr)
fmt.Println("origin:" + origin)
cfg, err := websocket.NewConfig(addr, origin, websocket_domain)
if err != nil {
return nil, err
}
cfg.Dialer = &net.Dialer{
Timeout: 10 * time.Second,
}
conn, err := websocket.DialConfig(cfg)
if err != nil {
return nil, err
}
return conn, nil
}
2.2.4 ConnectServerByProxy函数修改
文件路径:util/net/conn.go
func ConnectServerByProxy(proxyURL string, websocket_domain string, protocol string, addr string) (c net.Conn, err error) {
switch protocol {
case "tcp":
return gnet.DialTcpByProxy(proxyURL, addr)
case "kcp":
// http proxy is not supported for kcp
return ConnectServer(protocol, addr)
case "websocket":
return ConnectWebsocketServer(addr, websocket_domain, false)
case "wss":
return ConnectWebsocketServer(addr, websocket_domain, true)
default:
return nil, fmt.Errorf("unsupport protocol: %s", protocol)
}
}
2.3 配置文件修改
2.3.1 解析配置文件
文件路径:models/config/client_common.go
if tmpStr, ok = conf.Get("common", "protocol"); ok {
// Now it only support tcp and kcp and websocket.
if tmpStr != "tcp" && tmpStr != "kcp" && tmpStr != "websocket" && tmpStr != "wss" {
err = fmt.Errorf("Parse conf error: invalid protocol")
return
}
cfg.Protocol = tmpStr
if tmpStr, ok = conf.Get("common", "websocket_domain"); ok {
cfg.Websocket_domain = tmpStr
}
}
2.4 修复证书错误
文件路径:websocket/dial.go
func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) {
switch config.Location.Scheme {
case "ws":
conn, err = dialer.Dial("tcp", parseAuthority(config.Location))
case "wss":
config.TlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig)
default:
err = ErrBadScheme
}
return
}
2.5 修改FRP特征
文件路径:utils/net/websocket.go
修改常量FrpWebsocketPath以改变FRP的默认特征。
3. 配置示例
3.1 客户端配置
[common]
server_addr = xxxxxxxxxxxxx
server_port = 443
token = xxx
websocket_domain = xxxxxxx
protocol = wss
tls_enable = true
[%s]
type = tcp
remote_port = %d
plugin = socks5
plugin_user = a
plugin_passwd = %s
use_encryption = true
use_compression = true
3.2 服务端配置
[common]
#绑定地址
bind_addr = 0.0.0.0
#TCP绑定端口
bind_port = 443
#连接密码
token = xxx
4. 注意事项
- 域前置使用:在使用域前置时,
server_addr应使用IP地址而非域名,否则无法正常回源 - 证书问题:使用WSS协议时会存在证书报错,已通过设置
InsecureSkipVerify: true解决 - CDN配置:必须将CDN回源协议配置为HTTP
- 协议支持:修改后支持tcp、kcp、websocket和wss协议