diff --git a/Makefile b/Makefile index 3877f95e..691cee26 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,10 @@ gotest: ci: go test -count=1 -p=1 -v ./tests/... -alltest: gotest ci +e2e: + ./hack/run-e2e.sh + +alltest: gotest ci e2e clean: rm -f ./bin/frpc diff --git a/README.md b/README.md index beb94391..13bbb825 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # frp [![Build Status](https://travis-ci.org/fatedier/frp.svg?branch=master)](https://travis-ci.org/fatedier/frp) +[![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases) [README](README.md) | [中文文档](README_zh.md) diff --git a/README_zh.md b/README_zh.md index 678638d3..4a37b370 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,998 +1,33 @@ # frp [![Build Status](https://travis-ci.org/fatedier/frp.svg?branch=master)](https://travis-ci.org/fatedier/frp) +[![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases) [README](README.md) | [中文文档](README_zh.md) -frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp 协议,为 http 和 https 应用协议提供了额外的能力,且尝试性支持了点对点穿透。 +frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 -## 目录 +## 为什么使用 frp ? - +通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括: -* [开发状态](#开发状态) -* [架构](#架构) -* [使用示例](#使用示例) - * [通过 ssh 访问公司内网机器](#通过-ssh-访问公司内网机器) - * [通过自定义域名访问部署于内网的 web 服务](#通过自定义域名访问部署于内网的-web-服务) - * [转发 DNS 查询请求](#转发-dns-查询请求) - * [转发 Unix 域套接字](#转发-unix-域套接字) - * [对外提供简单的文件访问服务](#对外提供简单的文件访问服务) - * [为本地 HTTP 服务启用 HTTPS](#为本地-http-服务启用-https) - * [安全地暴露内网服务](#安全地暴露内网服务) - * [点对点内网穿透](#点对点内网穿透) -* [功能说明](#功能说明) - * [配置文件](#配置文件) - * [配置文件模版渲染](#配置文件模版渲染) - * [Dashboard](#dashboard) - * [Admin UI](#admin-ui) - * [监控](#监控) - * [Prometheus](#prometheus) - * [客户端身份验证](#客户端身份验证) - * [Token](#token) - * [OIDC](#oidc) - * [加密与压缩](#加密与压缩) - * [TLS](#tls) - * [客户端热加载配置文件](#客户端热加载配置文件) - * [客户端查看代理状态](#客户端查看代理状态) - * [端口白名单](#端口白名单) - * [端口复用](#端口复用) - * [限速](#限速) - * [代理限速](#代理限速) - * [TCP 多路复用](#tcp-多路复用) - * [底层通信可选 kcp 协议](#底层通信可选-kcp-协议) - * [连接池](#连接池) - * [负载均衡](#负载均衡) - * [健康检查](#健康检查) - * [修改 Host Header](#修改-host-header) - * [设置 HTTP 请求的 header](#设置-http-请求的-header) - * [获取用户真实 IP](#获取用户真实-ip) - * [HTTP X-Forwarded-For](#http-x-forwarded-for) - * [Proxy Protocol](#proxy-protocol) - * [通过密码保护你的 web 服务](#通过密码保护你的-web-服务) - * [自定义二级域名](#自定义二级域名) - * [URL 路由](#url-路由) - * [TCP 端口复用类型](#tcp-端口复用类型) - * [通过代理连接 frps](#通过代理连接-frps) - * [范围端口映射](#范围端口映射) - * [客户端插件](#客户端插件) - * [服务端管理插件](#服务端管理插件) -* [开发计划](#开发计划) -* [为 frp 做贡献](#为-frp-做贡献) -* [捐助](#捐助) - * [知识星球](#知识星球) - * [支付宝扫码捐赠](#支付宝扫码捐赠) - * [微信支付捐赠](#微信支付捐赠) - * [Paypal 捐赠](#paypal-捐赠) - - +* 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。 +* 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。 +* 代理组间的负载均衡。 +* 端口复用,多个服务通过同一个服务端端口暴露。 +* 多个原生支持的客户端插件(静态文件查看,HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。 +* 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。 +* 服务端和客户端 UI 页面。 ## 开发状态 -frp 仍然处于开发阶段,未经充分测试与验证,不推荐用于生产环境。 +frp 目前已被很多公司广泛用于测试、生产环境。 master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。 -**目前的交互协议可能随时改变,不保证向后兼容,升级新版本时需要注意公告说明同时升级服务端和客户端。** +## 文档 -## 架构 - -![architecture](/doc/pic/architecture.png) - -## 使用示例 - -根据对应的操作系统及架构,从 [Release](https://github.com/fatedier/frp/releases) 页面下载最新版本的程序。 - -将 **frps** 及 **frps.ini** 放到具有公网 IP 的机器上。 - -将 **frpc** 及 **frpc.ini** 放到处于内网环境的机器上。 - -### 通过 ssh 访问公司内网机器 - -1. 修改 frps.ini 文件,这里使用了最简化的配置,设置了 frp 服务器端接收客户端流量的端口: - - ```ini - # frps.ini - [common] - bind_port = 7000 - ``` - -2. 启动 frps: - - `./frps -c ./frps.ini` - -3. 修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [ssh] - type = tcp - local_ip = 127.0.0.1 - local_port = 22 - remote_port = 6000 - ``` - -注意,`local_port`(客户端侦听)和 `remote_port`(服务器端暴露)是用来出入 frp 系统的两端,`server_port` 则是服务器用来与客户端通讯的。 - - -4. 启动 frpc: - - `./frpc -c ./frpc.ini` - -5. 通过 ssh 访问内网机器,假设用户名为 test: - - `ssh -oPort=6000 test@x.x.x.x` - -### 通过自定义域名访问部署于内网的 web 服务 - -有时想要让其他人通过域名访问或者测试我们在本地搭建的 web 服务,但是由于本地机器没有公网 IP,无法将域名解析到本地的机器,通过 frp 就可以实现这一功能,以下示例为 http 服务,https 服务配置方法相同, vhost_http_port 替换为 vhost_https_port, type 设置为 https 即可。 - -1. 修改 frps.ini 文件,设置 http 访问端口为 8080: - - ```ini - # frps.ini - [common] - bind_port = 7000 - vhost_http_port = 8080 - ``` - -2. 启动 frps: - - `./frps -c ./frps.ini` - -3. 修改 frpc.ini 文件,假设 frps 所在的服务器的 IP 为 x.x.x.x,local_port 为本地机器上 web 服务对应的端口, 绑定自定义域名 `www.yourdomain.com`: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [web] - type = http - local_port = 80 - custom_domains = www.yourdomain.com - ``` - -4. 启动 frpc: - - `./frpc -c ./frpc.ini` - -5. 将 `www.yourdomain.com` 的域名 A 记录解析到 IP `x.x.x.x`,如果服务器已经有对应的域名,也可以将 CNAME 记录解析到服务器原先的域名。 - -6. 通过浏览器访问 `http://www.yourdomain.com:8080` 即可访问到处于内网机器上的 web 服务。 - -### 转发 DNS 查询请求 - -DNS 查询请求通常使用 UDP 协议,frp 支持对内网 UDP 服务的穿透,配置方式和 TCP 基本一致。 - -1. 修改 frps.ini 文件: - - ```ini - # frps.ini - [common] - bind_port = 7000 - ``` - -2. 启动 frps: - - `./frps -c ./frps.ini` - -3. 修改 frpc.ini 文件,设置 frps 所在服务器的 IP 为 x.x.x.x,转发到 Google 的 DNS 查询服务器 `8.8.8.8` 的 udp 53 端口: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [dns] - type = udp - local_ip = 8.8.8.8 - local_port = 53 - remote_port = 6000 - ``` - -4. 启动 frpc: - - `./frpc -c ./frpc.ini` - -5. 通过 dig 测试 UDP 包转发是否成功,预期会返回 `www.google.com` 域名的解析结果: - - `dig @x.x.x.x -p 6000 www.google.com` - -### 转发 Unix 域套接字 - -通过 tcp 端口访问内网的 unix域套接字(例如和 docker daemon 通信)。 - -frps 的部署步骤同上。 - -1. 启动 frpc,启用 `unix_domain_socket` 插件,配置如下: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [unix_domain_socket] - type = tcp - remote_port = 6000 - plugin = unix_domain_socket - plugin_unix_path = /var/run/docker.sock - ``` - -2. 通过 curl 命令查看 docker 版本信息 - - `curl http://x.x.x.x:6000/version` - -### 对外提供简单的文件访问服务 - -通过 `static_file` 插件可以对外提供一个简单的基于 HTTP 的文件访问服务。 - -frps 的部署步骤同上。 - -1. 启动 frpc,启用 `static_file` 插件,配置如下: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [test_static_file] - type = tcp - remote_port = 6000 - plugin = static_file - # 要对外暴露的文件目录 - plugin_local_path = /tmp/file - # 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径 - plugin_strip_prefix = static - plugin_http_user = abc - plugin_http_passwd = abc - ``` - -2. 通过浏览器访问 `http://x.x.x.x:6000/static/` 来查看位于 `/tmp/file` 目录下的文件,会要求输入已设置好的用户名和密码。 - -### 为本地 HTTP 服务启用 HTTPS - -通过 `https2http` 插件可以让本地 HTTP 服务转换成 HTTPS 服务对外提供。 - -1. 启用 frpc,启用 `https2http` 插件,配置如下: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [test_htts2http] - type = https - custom_domains = test.yourdomain.com - - plugin = https2http - plugin_local_addr = 127.0.0.1:80 - - # HTTPS 证书相关的配置 - plugin_crt_path = ./server.crt - plugin_key_path = ./server.key - plugin_host_header_rewrite = 127.0.0.1 - plugin_header_X-From-Where = frp - ``` - -2. 通过浏览器访问 `https://test.yourdomain.com` 即可。 - -### 安全地暴露内网服务 - -对于某些服务来说如果直接暴露于公网上将会存在安全隐患。 - -使用 **stcp(secret tcp)** 类型的代理可以避免让任何人都能访问到要穿透的服务,但是访问者也需要运行另外一个 frpc。 - -以下示例将会创建一个只有自己能访问到的 ssh 服务代理。 - -frps 的部署步骤同上。 - -1. 启动 frpc,转发内网的 ssh 服务,配置如下,不需要指定远程端口: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [secret_ssh] - type = stcp - # 只有 sk 一致的用户才能访问到此服务 - sk = abcdefg - local_ip = 127.0.0.1 - local_port = 22 - ``` - -2. 在要访问这个服务的机器上启动另外一个 frpc,配置如下: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [secret_ssh_visitor] - type = stcp - # stcp 的访问者 - role = visitor - # 要访问的 stcp 代理的名字 - server_name = secret_ssh - sk = abcdefg - # 绑定本地端口用于访问 ssh 服务 - bind_addr = 127.0.0.1 - bind_port = 6000 - ``` - -3. 通过 ssh 访问内网机器,假设用户名为 test: - - `ssh -oPort=6000 test@127.0.0.1` - -### 点对点内网穿透 - -frp 提供了一种新的代理类型 **xtcp** 用于应对在希望传输大量数据且流量不经过服务器的场景。 - -使用方式同 **stcp** 类似,需要在两边都部署上 frpc 用于建立直接的连接。 - -目前处于开发的初级阶段,并不能穿透所有类型的 NAT 设备,所以穿透成功率较低。穿透失败时可以尝试 **stcp** 的方式。 - -1. frps 除正常配置外需要额外配置一个 udp 端口用于支持该类型的客户端: - - ```ini - bind_udp_port = 7001 - ``` - -2. 启动 frpc,转发内网的 ssh 服务,配置如下,不需要指定远程端口: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [p2p_ssh] - type = xtcp - # 只有 sk 一致的用户才能访问到此服务 - sk = abcdefg - local_ip = 127.0.0.1 - local_port = 22 - ``` - -3. 在要访问这个服务的机器上启动另外一个 frpc,配置如下: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - server_port = 7000 - - [p2p_ssh_visitor] - type = xtcp - # xtcp 的访问者 - role = visitor - # 要访问的 xtcp 代理的名字 - server_name = p2p_ssh - sk = abcdefg - # 绑定本地端口用于访问 ssh 服务 - bind_addr = 127.0.0.1 - bind_port = 6000 - ``` - -4. 通过 ssh 访问内网机器,假设用户名为 test: - - `ssh -oPort=6000 test@127.0.0.1` - -## 功能说明 - -### 配置文件 - -由于 frp 目前支持的功能和配置项较多,未在文档中列出的功能可以从完整的示例配置文件中发现。 - -[frps 完整配置文件](./conf/frps_full.ini) - -[frpc 完整配置文件](./conf/frpc_full.ini) - -### 配置文件模版渲染 - -配置文件支持使用系统环境变量进行模版渲染,模版格式采用 Go 的标准格式。 - -示例配置如下: - -```ini -# frpc.ini -[common] -server_addr = {{ .Envs.FRP_SERVER_ADDR }} -server_port = 7000 - -[ssh] -type = tcp -local_ip = 127.0.0.1 -local_port = 22 -remote_port = {{ .Envs.FRP_SSH_REMOTE_PORT }} -``` - -启动 frpc 程序: - -``` -export FRP_SERVER_ADDR="x.x.x.x" -export FRP_SSH_REMOTE_PORT="6000" -./frpc -c ./frpc.ini -``` - -frpc 会自动使用环境变量渲染配置文件模版,所有环境变量需要以 `.Envs` 为前缀。 - -### Dashboard - -通过浏览器查看 frp 的状态以及代理统计信息展示。 - -**注:Dashboard 尚未针对大量的 proxy 数据展示做优化,如果出现 Dashboard 访问较慢的情况,请不要启用此功能。** - -需要在 frps.ini 中指定 dashboard 服务使用的端口,即可开启此功能: - -```ini -[common] -dashboard_port = 7500 -# dashboard 用户名密码,默认都为 admin -dashboard_user = admin -dashboard_pwd = admin -``` - -打开浏览器通过 `http://[server_addr]:7500` 访问 dashboard 界面,用户名密码默认为 `admin`。 - -![dashboard](/doc/pic/dashboard.png) - -### 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 端口映射出去即可,但需要重视安全风险。 - -### 监控 - -frps 当启用 Dashboard 后,会默认开启内部的监控,数据存放在内存中,每次重启进程后会清空,监控数据可以通过 dashboard 的地址发送 HTTP 请求获取。 - -目前还支持 Prometheus 作为可选的监控系统。 - -#### Prometheus - -在 `frps.ini` 中启用 Dashboard,并且设置 `enable_prometheus = true`,则通过 `http://{dashboard_addr}/metrics` 可以获取到 Prometheus 的监控数据。 - -### 客户端身份验证 - -目前 frpc 和 frps 之间支持两种身份验证方式,`token` 和 `oidc`。 - -通过 `frpc.ini` 和 `frps.ini` 中 `[common]` section 的 `authentication_method` 参数配置需要使用的验证方法。 - -`authenticate_heartbeats = true` 将会在每一个心跳包中附加上鉴权信息。 - -`authenticate_new_work_conns = true` 将会在每次建立新的工作连接时附加上鉴权信息。 - -#### Token - -当 `authentication_method = token`,将会启用基于 token 的验证方式。 - -需要在 `frpc.ini` 和 `frps.ini` 的 `[common]` section 中设置相同的 `token`。 - -#### OIDC - -当 `authentication_method = oidc`,将会启用基于 OIDC 的身份验证。 - -验证流程参考 [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4) - -启用这一验证方式,配置 `frpc.ini` 和 `frps.ini` 如下: - -```ini -# frps.ini -[common] -authentication_method = oidc -oidc_issuer = https://example-oidc-issuer.com/ -oidc_audience = https://oidc-audience.com/.default -``` - -```ini -[common] -authentication_method = oidc -oidc_client_id = 98692467-37de-409a-9fac-bb2585826f18 # Replace with OIDC client ID -oidc_client_secret = oidc_secret -oidc_audience = https://oidc-audience.com/.default -oidc_token_endpoint_url = https://example-oidc-endpoint.com/oauth2/v2.0/token -``` - -### 加密与压缩 - -这两个功能默认是不开启的,需要在 frpc.ini 中通过配置来为指定的代理启用加密与压缩的功能,压缩算法使用 snappy: - -```ini -# frpc.ini -[ssh] -type = tcp -local_port = 22 -remote_port = 6000 -use_encryption = true -use_compression = true -``` - -如果公司内网防火墙对外网访问进行了流量识别与屏蔽,例如禁止了 ssh 协议等,通过设置 `use_encryption = true`,将 frpc 与 frps 之间的通信内容加密传输,将会有效防止流量被拦截。 - -如果传输的报文长度较长,通过设置 `use_compression = true` 对传输内容进行压缩,可以有效减小 frpc 与 frps 之间的网络流量,加快流量转发速度,但是会额外消耗一些 cpu 资源。 - -#### TLS - -从 v0.25.0 版本开始 frpc 和 frps 之间支持通过 TLS 协议加密传输。通过在 `frpc.ini` 的 `common` 中配置 `tls_enable = true` 来启用此功能,安全性更高。 - -为了端口复用,frp 建立 TLS 连接的第一个字节为 0x17。 - -通过将 frps.ini 的 `[common]` 中 `tls_only` 设置为 true,可以强制 frps 只接受 TLS 连接。 - -**注意: 启用此功能后除 xtcp 外,不需要再设置 use_encryption。** - -### 客户端热加载配置文件 - -当修改了 frpc 中的代理配置,可以通过 `frpc reload` 命令来动态加载配置文件,通常会在 10 秒内完成代理的更新。 - -启用此功能需要在 frpc 中启用 admin 端口,用于提供 API 服务。配置如下: - -```ini -# frpc.ini -[common] -admin_addr = 127.0.0.1 -admin_port = 7400 -``` - -之后执行重启命令: - -`frpc reload -c ./frpc.ini` - -等待一段时间后客户端会根据新的配置文件创建、更新、删除代理。 - -**需要注意的是,[common] 中的参数除了 start 外目前无法被修改。** - -### 客户端查看代理状态 - -frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。 - -### 端口白名单 - -为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 `allow_ports` 来指定: - -```ini -# frps.ini -[common] -allow_ports = 2000-3000,3001,3003,4000-50000 -``` - -`allow_ports` 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。 - -### 端口复用 - -目前 frps 中的 `vhost_http_port` 和 `vhost_https_port` 支持配置成和 `bind_port` 为同一个端口,frps 会对连接的协议进行分析,之后进行不同的处理。 - -例如在某些限制较严格的网络环境中,可以将 `bind_port` 和 `vhost_https_port` 都设置为 443。 - -后续会尝试允许多个 proxy 绑定同一个远端端口的不同协议。 - -### 限速 - -#### 代理限速 - -目前支持在客户端的代理配置中设置代理级别的限速,限制单个 proxy 可以占用的带宽。 - -```ini -# frpc.ini -[ssh] -type = tcp -local_port = 22 -remote_port = 6000 -bandwidth_limit = 1MB -``` - -在代理配置中增加 `bandwidth_limit` 字段启用此功能,目前仅支持 `MB` 和 `KB` 单位。 - -### TCP 多路复用 - -从 v0.10.0 版本开始,客户端和服务器端之间的连接支持多路复用,不再需要为每一个用户请求创建一个连接,使连接建立的延迟降低,并且避免了大量文件描述符的占用,使 frp 可以承载更高的并发数。 - -该功能默认启用,如需关闭,可以在 frps.ini 和 frpc.ini 中配置,该配置项在服务端和客户端必须一致: - -```ini -# frps.ini 和 frpc.ini 中 -[common] -tcp_mux = false -``` - -### 底层通信可选 kcp 协议 - -底层通信协议支持选择 kcp 协议,在弱网环境下传输效率提升明显,但是会有一些额外的流量消耗。 - -开启 kcp 协议支持: - -1. 在 frps.ini 中启用 kcp 协议支持,指定一个 udp 端口用于接收客户端请求: - - ```ini - # frps.ini - [common] - bind_port = 7000 - # kcp 绑定的是 udp 端口,可以和 bind_port 一样 - kcp_bind_port = 7000 - ``` - -2. 在 frpc.ini 指定需要使用的协议类型,目前只支持 tcp 和 kcp。其他代理配置不需要变更: - - ```ini - # frpc.ini - [common] - server_addr = x.x.x.x - # server_port 指定为 frps 的 kcp_bind_port - server_port = 7000 - protocol = kcp - ``` - -3. 像之前一样使用 frp,需要注意开放相关机器上的 udp 的端口的访问权限。 - -### 连接池 - -默认情况下,当用户请求建立连接后,frps 才会请求 frpc 主动与后端服务建立一个连接。当为指定的代理启用连接池后,frp 会预先和后端服务建立起指定数量的连接,每次接收到用户请求后,会从连接池中取出一个连接和用户连接关联起来,避免了等待与后端服务建立连接以及 frpc 和 frps 之间传递控制信息的时间。 - -这一功能比较适合有大量短连接请求时开启。 - -1. 首先可以在 frps.ini 中设置每个代理可以创建的连接池上限,避免大量资源占用,客户端设置超过此配置后会被调整到当前值: - - ```ini - # frps.ini - [common] - max_pool_count = 5 - ``` - -2. 在 frpc.ini 中为客户端启用连接池,指定预创建连接的数量: - - ```ini - # frpc.ini - [common] - pool_count = 1 - ``` - -### 负载均衡 - -可以将多个相同类型的 proxy 加入到同一个 group 中,从而实现负载均衡的功能。 - -目前只支持 TCP 和 HTTP 类型的 proxy。 - -```ini -# frpc.ini -[test1] -type = tcp -local_port = 8080 -remote_port = 80 -group = web -group_key = 123 - -[test2] -type = tcp -local_port = 8081 -remote_port = 80 -group = web -group_key = 123 -``` - -用户连接 frps 服务器的 80 端口,frps 会将接收到的用户连接随机分发给其中一个存活的 proxy。这样可以在一台 frpc 机器挂掉后仍然有其他节点能够提供服务。 - -TCP 类型代理要求 `group_key` 相同,做权限验证,且 `remote_port` 相同。 - -HTTP 类型代理要求 `group_key, custom_domains 或 subdomain 和 locations` 相同。 - -### 健康检查 - -通过给 proxy 加上健康检查的功能,可以在要反向代理的服务出现故障时,将这个服务从 frps 中摘除,搭配负载均衡的功能,可以用来实现高可用的架构,避免服务单点故障。 - -在每一个 proxy 的配置下加上 `health_check_type = {type}` 来启用健康检查功能。 - -**type** 目前可选 tcp 和 http。 - -tcp 只要能够建立连接则认为服务正常,http 会发送一个 http 请求,服务需要返回 2xx 的状态码才会被认为正常。 - -tcp 示例配置如下: - -```ini -# frpc.ini -[test1] -type = tcp -local_port = 22 -remote_port = 6000 -# 启用健康检查,类型为 tcp -health_check_type = tcp -# 建立连接超时时间为 3 秒 -health_check_timeout_s = 3 -# 连续 3 次检查失败,此 proxy 会被摘除 -health_check_max_failed = 3 -# 每隔 10 秒进行一次健康检查 -health_check_interval_s = 10 -``` - -http 示例配置如下: - -```ini -# frpc.ini -[web] -type = http -local_ip = 127.0.0.1 -local_port = 80 -custom_domains = test.yourdomain.com -# 启用健康检查,类型为 http -health_check_type = http -# 健康检查发送 http 请求的 url,后端服务需要返回 2xx 的 http 状态码 -health_check_url = /status -health_check_interval_s = 10 -health_check_max_failed = 3 -health_check_timeout_s = 3 -``` - -### 修改 Host Header - -通常情况下 frp 不会修改转发的任何数据。但有一些后端服务会根据 http 请求 header 中的 host 字段来展现不同的网站,例如 nginx 的虚拟主机服务,启用 host-header 的修改功能可以动态修改 http 请求中的 host 字段。该功能仅限于 http 类型的代理。 - -```ini -# frpc.ini -[web] -type = http -local_port = 80 -custom_domains = test.yourdomain.com -host_header_rewrite = dev.yourdomain.com -``` - -原来 http 请求中的 host 字段 `test.yourdomain.com` 转发到后端服务时会被替换为 `dev.yourdomain.com`。 - -### 设置 HTTP 请求的 header - -对于 `type = http` 的代理,可以设置在转发中动态添加的 header 参数。 - -```ini -# frpc.ini -[web] -type = http -local_port = 80 -custom_domains = test.yourdomain.com -host_header_rewrite = dev.yourdomain.com -header_X-From-Where = frp -``` - -对于参数配置中所有以 `header_` 开头的参数(支持同时配置多个),都会被添加到 http 请求的 header 中,根据如上的配置,会在请求的 header 中加上 `X-From-Where: frp`。 - -### 获取用户真实 IP - -#### HTTP X-Forwarded-For - -目前只有 **http** 类型的代理支持这一功能,可以通过用户请求的 header 中的 `X-Forwarded-For` 来获取用户真实 IP,默认启用。 - -#### Proxy Protocol - -frp 支持通过 **Proxy Protocol** 协议来传递经过 frp 代理的请求的真实 IP,此功能支持所有以 TCP 为底层协议的类型,不支持 UDP。 - -**Proxy Protocol** 功能启用后,frpc 在和本地服务建立连接后,会先发送一段 **Proxy Protocol** 的协议内容给本地服务,本地服务通过解析这一内容可以获得访问用户的真实 IP。所以不仅仅是 HTTP 服务,任何的 TCP 服务,只要支持这一协议,都可以获得用户的真实 IP 地址。 - -需要注意的是,在代理配置中如果要启用此功能,需要本地的服务能够支持 **Proxy Protocol** 这一协议,目前 nginx 和 haproxy 都能够很好的支持。 - -这里以 https 类型为例: - -```ini -# frpc.ini -[web] -type = https -local_port = 443 -custom_domains = test.yourdomain.com - -# 目前支持 v1 和 v2 两个版本的 proxy protocol 协议。 -proxy_protocol_version = v2 -``` - -只需要在代理配置中增加一行 `proxy_protocol_version = v2` 即可开启此功能。 - -本地的 https 服务可以通过在 nginx 的配置中启用 **Proxy Protocol** 的解析并将结果设置在 `X-Real-IP` 这个 Header 中就可以在自己的 Web 服务中通过 `X-Real-IP` 获取到用户的真实 IP。 - -### 通过密码保护你的 web 服务 - -由于所有客户端共用一个 frps 的 http 服务端口,任何知道你的域名和 url 的人都能访问到你部署在内网的 web 服务,但是在某些场景下需要确保只有限定的用户才能访问。 - -frp 支持通过 HTTP Basic Auth 来保护你的 web 服务,使用户需要通过用户名和密码才能访问到你的服务。 - -该功能目前仅限于 http 类型的代理,需要在 frpc 的代理配置中添加用户名和密码的设置。 - -```ini -# frpc.ini -[web] -type = http -local_port = 80 -custom_domains = test.yourdomain.com -http_user = abc -http_pwd = abc -``` - -通过浏览器访问 `http://test.yourdomain.com`,需要输入配置的用户名和密码才能访问。 - -### 自定义二级域名 - -在多人同时使用一个 frps 时,通过自定义二级域名的方式来使用会更加方便。 - -通过在 frps 的配置文件中配置 `subdomain_host`,就可以启用该特性。之后在 frpc 的 http、https 类型的代理中可以不配置 `custom_domains`,而是配置一个 `subdomain` 参数。 - -只需要将 `*.{subdomain_host}` 解析到 frps 所在服务器。之后用户可以通过 `subdomain` 自行指定自己的 web 服务所需要使用的二级域名,通过 `{subdomain}.{subdomain_host}` 来访问自己的 web 服务。 - -```ini -# frps.ini -[common] -subdomain_host = frps.com -``` - -将泛域名 `*.frps.com` 解析到 frps 所在服务器的 IP 地址。 - -```ini -# frpc.ini -[web] -type = http -local_port = 80 -subdomain = test -``` - -frps 和 frpc 都启动成功后,通过 `test.frps.com` 就可以访问到内网的 web 服务。 - -**注:如果 frps 配置了 `subdomain_host`,则 `custom_domains` 中不能是属于 `subdomain_host` 的子域名或者泛域名。** - -同一个 http 或 https 类型的代理中 `custom_domains` 和 `subdomain` 可以同时配置。 - -### URL 路由 - -frp 支持根据请求的 URL 路径路由转发到不同的后端服务。 - -通过配置文件中的 `locations` 字段指定一个或多个 proxy 能够匹配的 URL 前缀(目前仅支持最大前缀匹配,之后会考虑正则匹配)。例如指定 `locations = /news`,则所有 URL 以 `/news` 开头的请求都会被转发到这个服务。 - -```ini -# frpc.ini -[web01] -type = http -local_port = 80 -custom_domains = web.yourdomain.com -locations = / - -[web02] -type = http -local_port = 81 -custom_domains = web.yourdomain.com -locations = /news,/about -``` - -按照上述的示例配置后,`web.yourdomain.com` 这个域名下所有以 `/news` 以及 `/about` 作为前缀的 URL 请求都会被转发到 web02,其余的请求会被转发到 web01。 - -### TCP 端口复用类型 - -frp 支持将单个端口收到的连接路由到不同的代理,类似 `vhost_http_port` 和 `vhost_https_port`。 - -目前支持的复用器只有 `httpconnect`。 - -当在 `frps.ini` 的 `[common]` 中设置 `tcpmux_httpconnect_port`,frps 将会监听在这个端口,接收 HTTP CONNECT 请求。 - -frps 会根据 HTTP CONNECT 请求中的 host 路由到不同的后端代理。 - -示例配置如下: - -```ini -# frps.ini -[common] -bind_port = 7000 -tcpmux_httpconnect_port = 1337 -``` - -```ini -# frpc.ini -[common] -server_addr = x.x.x.x -server_port = 7000 - -[proxy1] -type = tcpmux -multiplexer = httpconnect -custom_domains = test1 - -[proxy2] -type = tcpmux -multiplexer = httpconnect -custom_domains = test2 -``` - -通过上面的配置,frps 如果接收到 HTTP CONNECT 请求内容: - -``` -CONNECT test1 HTTP/1.1\r\n\r\n -``` - -该连接将会被路由到 proxy1 。 - -### 通过代理连接 frps - -在只能通过代理访问外网的环境内,frpc 支持通过 HTTP PROXY 和 frps 进行通信。 - -可以通过设置 `HTTP_PROXY` 系统环境变量或者通过在 frpc 的配置文件中设置 `http_proxy` 参数来使用此功能。 - -仅在 `protocol = tcp` 时生效。 - -```ini -# frpc.ini -[common] -server_addr = x.x.x.x -server_port = 7000 -http_proxy = http://user:pwd@192.168.1.128:8080 -``` - -### 范围端口映射 - -在 frpc 的配置文件中可以指定映射多个端口,目前只支持 tcp 和 udp 的类型。 - -这一功能通过 `range:` 段落标记来实现,客户端会解析这个标记中的配置,将其拆分成多个 proxy,每一个 proxy 以数字为后缀命名。 - -例如要映射本地 6000-6005, 6007 这6个端口,主要配置如下: - -```ini -# frpc.ini -[range:test_tcp] -type = tcp -local_ip = 127.0.0.1 -local_port = 6000-6006,6007 -remote_port = 6000-6006,6007 -``` - -实际连接成功后会创建 8 个 proxy,命名为 `test_tcp_0, test_tcp_1 ... test_tcp_7`。 - -### 客户端插件 - -默认情况下,frpc 只会转发请求到本地 tcp 或 udp 端口。 - -客户端插件模式是为了在客户端提供更加丰富的功能,目前内置的插件有 `unix_domain_socket`、`http_proxy`、`socks5`、`static_file`。具体使用方式请查看[使用示例](#使用示例)。 - -通过 `plugin` 指定需要使用的插件,插件的配置参数都以 `plugin_` 开头。使用插件后 `local_ip` 和 `local_port` 不再需要配置。 - -使用 **http_proxy** 插件的示例: - -```ini -# frpc.ini -[http_proxy] -type = tcp -remote_port = 6000 -plugin = http_proxy -plugin_http_user = abc -plugin_http_passwd = abc -``` - -`plugin_http_user` 和 `plugin_http_passwd` 即为 `http_proxy` 插件可选的配置参数。 - -### 服务端管理插件 - -[使用说明](/doc/server_plugin_zh.md) - -从 [gofrp/plugin](https://github.com/gofrp/plugin) 中寻找更多插件。 - -## 开发计划 - -计划在后续版本中加入的功能与优化,排名不分先后,如果有其他功能建议欢迎在 [issues](https://github.com/fatedier/frp/issues) 中反馈。 - -* frps 记录 http 请求日志。 +完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。 ## 为 frp 做贡献 @@ -1003,7 +38,7 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进 * 如果是增加新的功能特性,请先创建一个 issue 并做简单描述以及大致的实现方法,提议被采纳后,就可以创建一个实现新特性的 Pull Request。 * 欢迎对说明文档做出改善,帮助更多的人使用 frp,特别是英文文档。 * 贡献代码请提交 PR 至 dev 分支,master 分支仅用于发布稳定可用版本。 -* 如果你有任何其他方面的问题,欢迎反馈至 fatedier@gmail.com 共同交流。 +* 如果你有任何其他方面的问题或合作,欢迎发送邮件至 fatedier@gmail.com 。 **提醒:和项目相关的问题最好在 [issues](https://github.com/fatedier/frp/issues) 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。** @@ -1013,7 +48,7 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进 ### 知识星球 -如果您想学习 frp 相关的知识和技术,或者寻求任何帮助,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群: +如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群: ![zsxq](/doc/pic/zsxq.jpg) diff --git a/client/admin.go b/client/admin.go index b3e3f81a..52babe23 100644 --- a/client/admin.go +++ b/client/admin.go @@ -36,7 +36,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) { router := mux.NewRouter() user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd - router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware) + router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) // api, see dashboard_api.go router.HandleFunc("/api/reload", svr.apiReload).Methods("GET") @@ -46,7 +46,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) { // view router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") - router.PathPrefix("/static/").Handler(frpNet.MakeHttpGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") + router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/static/", http.StatusMovedPermanently) }) diff --git a/client/admin_api.go b/client/admin_api.go index 1acbb95b..14626736 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -82,13 +82,13 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { } type StatusResp struct { - Tcp []ProxyStatusResp `json:"tcp"` - Udp []ProxyStatusResp `json:"udp"` - Http []ProxyStatusResp `json:"http"` - Https []ProxyStatusResp `json:"https"` - Stcp []ProxyStatusResp `json:"stcp"` - Xtcp []ProxyStatusResp `json:"xtcp"` - Sudp []ProxyStatusResp `json:"sudp"` + TCP []ProxyStatusResp `json:"tcp"` + UDP []ProxyStatusResp `json:"udp"` + HTTP []ProxyStatusResp `json:"http"` + HTTPS []ProxyStatusResp `json:"https"` + STCP []ProxyStatusResp `json:"stcp"` + XTCP []ProxyStatusResp `json:"xtcp"` + SUDP []ProxyStatusResp `json:"sudp"` } type ProxyStatusResp struct { @@ -107,17 +107,17 @@ func (a ByProxyStatusResp) Len() int { return len(a) } func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 } -func NewProxyStatusResp(status *proxy.ProxyStatus, serverAddr string) ProxyStatusResp { +func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp { psr := ProxyStatusResp{ Name: status.Name, Type: status.Type, - Status: status.Status, + Status: status.Phase, Err: status.Err, } switch cfg := status.Cfg.(type) { - case *config.TcpProxyConf: + case *config.TCPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin if status.Err != "" { @@ -125,40 +125,40 @@ func NewProxyStatusResp(status *proxy.ProxyStatus, serverAddr string) ProxyStatu } else { psr.RemoteAddr = serverAddr + status.RemoteAddr } - case *config.UdpProxyConf: + case *config.UDPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } if status.Err != "" { psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort) } else { psr.RemoteAddr = serverAddr + status.RemoteAddr } - case *config.HttpProxyConf: + case *config.HTTPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin psr.RemoteAddr = status.RemoteAddr - case *config.HttpsProxyConf: + case *config.HTTPSProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin psr.RemoteAddr = status.RemoteAddr - case *config.StcpProxyConf: + case *config.STCPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin - case *config.XtcpProxyConf: + case *config.XTCPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin - case *config.SudpProxyConf: + case *config.SUDPProxyConf: if cfg.LocalPort != 0 { - psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) + psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) } psr.Plugin = cfg.Plugin } @@ -171,13 +171,13 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { buf []byte res StatusResp ) - res.Tcp = make([]ProxyStatusResp, 0) - res.Udp = make([]ProxyStatusResp, 0) - res.Http = make([]ProxyStatusResp, 0) - res.Https = make([]ProxyStatusResp, 0) - res.Stcp = make([]ProxyStatusResp, 0) - res.Xtcp = make([]ProxyStatusResp, 0) - res.Sudp = make([]ProxyStatusResp, 0) + res.TCP = make([]ProxyStatusResp, 0) + res.UDP = make([]ProxyStatusResp, 0) + res.HTTP = make([]ProxyStatusResp, 0) + res.HTTPS = make([]ProxyStatusResp, 0) + res.STCP = make([]ProxyStatusResp, 0) + res.XTCP = make([]ProxyStatusResp, 0) + res.SUDP = make([]ProxyStatusResp, 0) log.Info("Http request [/api/status]") defer func() { @@ -190,28 +190,28 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { for _, status := range ps { switch status.Type { case "tcp": - res.Tcp = append(res.Tcp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.TCP = append(res.TCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "udp": - res.Udp = append(res.Udp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.UDP = append(res.UDP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "http": - res.Http = append(res.Http, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.HTTP = append(res.HTTP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "https": - res.Https = append(res.Https, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.HTTPS = append(res.HTTPS, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "stcp": - res.Stcp = append(res.Stcp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.STCP = append(res.STCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "xtcp": - res.Xtcp = append(res.Xtcp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.XTCP = append(res.XTCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) case "sudp": - res.Sudp = append(res.Sudp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) + res.SUDP = append(res.SUDP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) } } - sort.Sort(ByProxyStatusResp(res.Tcp)) - sort.Sort(ByProxyStatusResp(res.Udp)) - sort.Sort(ByProxyStatusResp(res.Http)) - sort.Sort(ByProxyStatusResp(res.Https)) - sort.Sort(ByProxyStatusResp(res.Stcp)) - sort.Sort(ByProxyStatusResp(res.Xtcp)) - sort.Sort(ByProxyStatusResp(res.Sudp)) + sort.Sort(ByProxyStatusResp(res.TCP)) + sort.Sort(ByProxyStatusResp(res.UDP)) + sort.Sort(ByProxyStatusResp(res.HTTP)) + sort.Sort(ByProxyStatusResp(res.HTTPS)) + sort.Sort(ByProxyStatusResp(res.STCP)) + sort.Sort(ByProxyStatusResp(res.XTCP)) + sort.Sort(ByProxyStatusResp(res.SUDP)) return } diff --git a/client/control.go b/client/control.go index 3da61712..861c9777 100644 --- a/client/control.go +++ b/client/control.go @@ -28,6 +28,7 @@ import ( "github.com/fatedier/frp/models/auth" "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" + "github.com/fatedier/frp/models/transport" frpNet "github.com/fatedier/frp/utils/net" "github.com/fatedier/frp/utils/xlog" @@ -38,11 +39,11 @@ import ( type Control struct { // uniq id got from frps, attach it in loginMsg - runId string + runID string // manage all proxies pxyCfgs map[string]config.ProxyConf - pm *proxy.ProxyManager + pm *proxy.Manager // manage all visitors vm *VisitorManager @@ -88,7 +89,7 @@ type Control struct { authSetter auth.Setter } -func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux.Session, +func NewControl(ctx context.Context, runID string, conn net.Conn, session *fmux.Session, clientCfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, @@ -97,7 +98,7 @@ func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux. // new xlog instance ctl := &Control{ - runId: runId, + runID: runID, conn: conn, session: session, pxyCfgs: pxyCfgs, @@ -114,7 +115,7 @@ func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux. ctx: ctx, authSetter: authSetter, } - ctl.pm = proxy.NewProxyManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort) + ctl.pm = proxy.NewManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort) ctl.vm = NewVisitorManager(ctl.ctx, ctl) ctl.vm.Reload(visitorCfgs) @@ -140,7 +141,7 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) { } m := &msg.NewWorkConn{ - RunId: ctl.runId, + RunID: ctl.runID, } if err = ctl.authSetter.SetNewWorkConn(m); err != nil { xl.Warn("error during NewWorkConn authentication: %v", err) @@ -198,7 +199,7 @@ func (ctl *Control) ClosedDoneCh() <-chan struct{} { // connectServer return a new connection to frps func (ctl *Control) connectServer() (conn net.Conn, err error) { xl := ctl.xl - if ctl.clientCfg.TcpMux { + if ctl.clientCfg.TCPMux { stream, errRet := ctl.session.OpenStream() if errRet != nil { err = errRet @@ -208,12 +209,19 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) { conn = stream } else { var tlsConfig *tls.Config + if ctl.clientCfg.TLSEnable { - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, + tlsConfig, err = transport.NewServerTLSConfig( + ctl.clientCfg.TLSCertFile, + ctl.clientCfg.TLSKeyFile, + ctl.clientCfg.TLSTrustedCaFile) + + if err != nil { + xl.Warn("fail to build tls configuration when connecting to server, err: %v", err) + return } } - conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HttpProxy, ctl.clientCfg.Protocol, + conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig) if err != nil { xl.Warn("start new connection to server error: %v", err) @@ -237,18 +245,17 @@ func (ctl *Control) reader() { encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token)) for { - if m, err := msg.ReadMsg(encReader); err != nil { + m, err := msg.ReadMsg(encReader) + if err != nil { if err == io.EOF { xl.Debug("read from control connection EOF") return - } else { - xl.Warn("read error: %v", err) - ctl.conn.Close() - return } - } else { - ctl.readCh <- m + xl.Warn("read error: %v", err) + ctl.conn.Close() + return } + ctl.readCh <- m } } @@ -263,14 +270,15 @@ func (ctl *Control) writer() { return } for { - if m, ok := <-ctl.sendCh; !ok { + m, ok := <-ctl.sendCh + if !ok { xl.Info("control writer is closing") return - } else { - if err := msg.WriteMsg(encWriter, m); err != nil { - xl.Warn("write message to control connection error: %v", err) - return - } + } + + if err := msg.WriteMsg(encWriter, m); err != nil { + xl.Warn("write message to control connection error: %v", err) + return } } } diff --git a/client/event/event.go b/client/event/event.go index e8ea2e20..71d61733 100644 --- a/client/event/event.go +++ b/client/event/event.go @@ -6,10 +6,10 @@ import ( "github.com/fatedier/frp/models/msg" ) -type EventType int +type Type int const ( - EvStartProxy EventType = iota + EvStartProxy Type = iota EvCloseProxy ) @@ -17,7 +17,7 @@ var ( ErrPayloadType = errors.New("error payload type") ) -type EventHandler func(evType EventType, payload interface{}) error +type Handler func(evType Type, payload interface{}) error type StartProxyPayload struct { NewProxyMsg *msg.NewProxy diff --git a/client/health/health.go b/client/health/health.go index 12771411..d2ae9103 100644 --- a/client/health/health.go +++ b/client/health/health.go @@ -31,7 +31,7 @@ var ( ErrHealthCheckType = errors.New("error health check type") ) -type HealthCheckMonitor struct { +type Monitor struct { checkType string interval time.Duration timeout time.Duration @@ -52,10 +52,10 @@ type HealthCheckMonitor struct { cancel context.CancelFunc } -func NewHealthCheckMonitor(ctx context.Context, checkType string, +func NewMonitor(ctx context.Context, checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string, - statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor { + statusNormalFn func(), statusFailedFn func()) *Monitor { if intervalS <= 0 { intervalS = 10 @@ -67,7 +67,7 @@ func NewHealthCheckMonitor(ctx context.Context, checkType string, maxFailedTimes = 1 } newctx, cancel := context.WithCancel(ctx) - return &HealthCheckMonitor{ + return &Monitor{ checkType: checkType, interval: time.Duration(intervalS) * time.Second, timeout: time.Duration(timeoutS) * time.Second, @@ -82,15 +82,15 @@ func NewHealthCheckMonitor(ctx context.Context, checkType string, } } -func (monitor *HealthCheckMonitor) Start() { +func (monitor *Monitor) Start() { go monitor.checkWorker() } -func (monitor *HealthCheckMonitor) Stop() { +func (monitor *Monitor) Stop() { monitor.cancel() } -func (monitor *HealthCheckMonitor) checkWorker() { +func (monitor *Monitor) checkWorker() { xl := xlog.FromContextSafe(monitor.ctx) for { doCtx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout)) @@ -126,18 +126,18 @@ func (monitor *HealthCheckMonitor) checkWorker() { } } -func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) error { +func (monitor *Monitor) doCheck(ctx context.Context) error { switch monitor.checkType { case "tcp": - return monitor.doTcpCheck(ctx) + return monitor.doTCPCheck(ctx) case "http": - return monitor.doHttpCheck(ctx) + return monitor.doHTTPCheck(ctx) default: return ErrHealthCheckType } } -func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) error { +func (monitor *Monitor) doTCPCheck(ctx context.Context) error { // if tcp address is not specified, always return nil if monitor.addr == "" { return nil @@ -152,7 +152,7 @@ func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) error { return nil } -func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) error { +func (monitor *Monitor) doHTTPCheck(ctx context.Context) error { req, err := http.NewRequest("GET", monitor.url, nil) if err != nil { return err diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go index 0c9ea52f..cb7d5177 100644 --- a/client/proxy/proxy.go +++ b/client/proxy/proxy.go @@ -67,43 +67,43 @@ func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.Cl ctx: ctx, } switch cfg := pxyConf.(type) { - case *config.TcpProxyConf: - pxy = &TcpProxy{ + case *config.TCPProxyConf: + pxy = &TCPProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.TcpMuxProxyConf: - pxy = &TcpMuxProxy{ + case *config.TCPMuxProxyConf: + pxy = &TCPMuxProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.UdpProxyConf: - pxy = &UdpProxy{ + case *config.UDPProxyConf: + pxy = &UDPProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.HttpProxyConf: - pxy = &HttpProxy{ + case *config.HTTPProxyConf: + pxy = &HTTPProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.HttpsProxyConf: - pxy = &HttpsProxy{ + case *config.HTTPSProxyConf: + pxy = &HTTPSProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.StcpProxyConf: - pxy = &StcpProxy{ + case *config.STCPProxyConf: + pxy = &STCPProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.XtcpProxyConf: - pxy = &XtcpProxy{ + case *config.XTCPProxyConf: + pxy = &XTCPProxy{ BaseProxy: &baseProxy, cfg: cfg, } - case *config.SudpProxyConf: - pxy = &SudpProxy{ + case *config.SUDPProxyConf: + pxy = &SUDPProxy{ BaseProxy: &baseProxy, cfg: cfg, closeCh: make(chan struct{}), @@ -124,14 +124,14 @@ type BaseProxy struct { } // TCP -type TcpProxy struct { +type TCPProxy struct { *BaseProxy - cfg *config.TcpProxyConf + cfg *config.TCPProxyConf proxyPlugin plugin.Plugin } -func (pxy *TcpProxy) Run() (err error) { +func (pxy *TCPProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -141,26 +141,26 @@ func (pxy *TcpProxy) Run() (err error) { return } -func (pxy *TcpProxy) Close() { +func (pxy *TCPProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *TcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, +func (pxy *TCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, conn, []byte(pxy.clientCfg.Token), m) } // TCP Multiplexer -type TcpMuxProxy struct { +type TCPMuxProxy struct { *BaseProxy - cfg *config.TcpMuxProxyConf + cfg *config.TCPMuxProxyConf proxyPlugin plugin.Plugin } -func (pxy *TcpMuxProxy) Run() (err error) { +func (pxy *TCPMuxProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -170,26 +170,26 @@ func (pxy *TcpMuxProxy) Run() (err error) { return } -func (pxy *TcpMuxProxy) Close() { +func (pxy *TCPMuxProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *TcpMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, +func (pxy *TCPMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, conn, []byte(pxy.clientCfg.Token), m) } // HTTP -type HttpProxy struct { +type HTTPProxy struct { *BaseProxy - cfg *config.HttpProxyConf + cfg *config.HTTPProxyConf proxyPlugin plugin.Plugin } -func (pxy *HttpProxy) Run() (err error) { +func (pxy *HTTPProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -199,26 +199,26 @@ func (pxy *HttpProxy) Run() (err error) { return } -func (pxy *HttpProxy) Close() { +func (pxy *HTTPProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *HttpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, +func (pxy *HTTPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, conn, []byte(pxy.clientCfg.Token), m) } // HTTPS -type HttpsProxy struct { +type HTTPSProxy struct { *BaseProxy - cfg *config.HttpsProxyConf + cfg *config.HTTPSProxyConf proxyPlugin plugin.Plugin } -func (pxy *HttpsProxy) Run() (err error) { +func (pxy *HTTPSProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -228,26 +228,26 @@ func (pxy *HttpsProxy) Run() (err error) { return } -func (pxy *HttpsProxy) Close() { +func (pxy *HTTPSProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *HttpsProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, +func (pxy *HTTPSProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, conn, []byte(pxy.clientCfg.Token), m) } // STCP -type StcpProxy struct { +type STCPProxy struct { *BaseProxy - cfg *config.StcpProxyConf + cfg *config.STCPProxyConf proxyPlugin plugin.Plugin } -func (pxy *StcpProxy) Run() (err error) { +func (pxy *STCPProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -257,26 +257,26 @@ func (pxy *StcpProxy) Run() (err error) { return } -func (pxy *StcpProxy) Close() { +func (pxy *STCPProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *StcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, +func (pxy *STCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, conn, []byte(pxy.clientCfg.Token), m) } // XTCP -type XtcpProxy struct { +type XTCPProxy struct { *BaseProxy - cfg *config.XtcpProxyConf + cfg *config.XTCPProxyConf proxyPlugin plugin.Plugin } -func (pxy *XtcpProxy) Run() (err error) { +func (pxy *XTCPProxy) Run() (err error) { if pxy.cfg.Plugin != "" { pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams) if err != nil { @@ -286,13 +286,13 @@ func (pxy *XtcpProxy) Run() (err error) { return } -func (pxy *XtcpProxy) Close() { +func (pxy *XTCPProxy) Close() { if pxy.proxyPlugin != nil { pxy.proxyPlugin.Close() } } -func (pxy *XtcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { +func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { xl := pxy.xl defer conn.Close() var natHoleSidMsg msg.NatHoleSid @@ -389,7 +389,7 @@ func (pxy *XtcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { lConn.WriteToUDP(sidBuf[:n], uAddr) - kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, false, uAddr.String()) + kcpConn, err := frpNet.NewKCPConnFromUDP(lConn, false, uAddr.String()) if err != nil { xl.Error("create kcp connection from udp connection error: %v", err) return @@ -410,11 +410,11 @@ func (pxy *XtcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { return } - HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, + HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter, muxConn, []byte(pxy.cfg.Sk), m) } -func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) { +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 @@ -434,28 +434,28 @@ func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, c } // UDP -type UdpProxy struct { +type UDPProxy struct { *BaseProxy - cfg *config.UdpProxyConf + cfg *config.UDPProxyConf localAddr *net.UDPAddr - readCh chan *msg.UdpPacket + readCh chan *msg.UDPPacket - // include msg.UdpPacket and msg.Ping + // include msg.UDPPacket and msg.Ping sendCh chan msg.Message workConn net.Conn } -func (pxy *UdpProxy) Run() (err error) { - pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIp, pxy.cfg.LocalPort)) +func (pxy *UDPProxy) Run() (err error) { + pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort)) if err != nil { return } return } -func (pxy *UdpProxy) Close() { +func (pxy *UDPProxy) Close() { pxy.mu.Lock() defer pxy.mu.Unlock() @@ -473,29 +473,42 @@ func (pxy *UdpProxy) Close() { } } -func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { +func (pxy *UDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { xl := pxy.xl xl.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String()) // close resources releated with old workConn pxy.Close() + var rwc io.ReadWriteCloser = conn + var err error if pxy.limiter != nil { - rwc := frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error { + rwc = frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error { return conn.Close() }) - conn = frpNet.WrapReadWriteCloserToConn(rwc, conn) } + if pxy.cfg.UseEncryption { + rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.clientCfg.Token)) + if err != nil { + conn.Close() + xl.Error("create encryption stream error: %v", err) + return + } + } + if pxy.cfg.UseCompression { + rwc = frpIo.WithCompression(rwc) + } + conn = frpNet.WrapReadWriteCloserToConn(rwc, conn) pxy.mu.Lock() pxy.workConn = conn - pxy.readCh = make(chan *msg.UdpPacket, 1024) + pxy.readCh = make(chan *msg.UDPPacket, 1024) pxy.sendCh = make(chan msg.Message, 1024) pxy.closed = false pxy.mu.Unlock() - workConnReaderFn := func(conn net.Conn, readCh chan *msg.UdpPacket) { + workConnReaderFn := func(conn net.Conn, readCh chan *msg.UDPPacket) { for { - var udpMsg msg.UdpPacket + var udpMsg msg.UDPPacket if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil { xl.Warn("read from workConn for udp error: %v", errRet) return @@ -516,7 +529,7 @@ func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { var errRet error for rawMsg := range sendCh { switch m := rawMsg.(type) { - case *msg.UdpPacket: + case *msg.UDPPacket: xl.Trace("send udp package to workConn: %s", m.Content) case *msg.Ping: xl.Trace("send ping message to udp workConn") @@ -543,28 +556,28 @@ func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { go workConnSenderFn(pxy.workConn, pxy.sendCh) go workConnReaderFn(pxy.workConn, pxy.readCh) go heartbeatFn(pxy.workConn, pxy.sendCh) - udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh) + udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize)) } -type SudpProxy struct { +type SUDPProxy struct { *BaseProxy - cfg *config.SudpProxyConf + cfg *config.SUDPProxyConf localAddr *net.UDPAddr closeCh chan struct{} } -func (pxy *SudpProxy) Run() (err error) { - pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIp, pxy.cfg.LocalPort)) +func (pxy *SUDPProxy) Run() (err error) { + pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort)) if err != nil { return } return } -func (pxy *SudpProxy) Close() { +func (pxy *SUDPProxy) Close() { pxy.mu.Lock() defer pxy.mu.Unlock() select { @@ -575,19 +588,32 @@ func (pxy *SudpProxy) Close() { } } -func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { +func (pxy *SUDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { xl := pxy.xl xl.Info("incoming a new work connection for sudp proxy, %s", conn.RemoteAddr().String()) + var rwc io.ReadWriteCloser = conn + var err error if pxy.limiter != nil { - rwc := frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error { + rwc = frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error { return conn.Close() }) - conn = frpNet.WrapReadWriteCloserToConn(rwc, conn) } + if pxy.cfg.UseEncryption { + rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.clientCfg.Token)) + if err != nil { + conn.Close() + xl.Error("create encryption stream error: %v", err) + return + } + } + if pxy.cfg.UseCompression { + rwc = frpIo.WithCompression(rwc) + } + conn = frpNet.WrapReadWriteCloserToConn(rwc, conn) workConn := conn - readCh := make(chan *msg.UdpPacket, 1024) + readCh := make(chan *msg.UDPPacket, 1024) sendCh := make(chan msg.Message, 1024) isClose := false @@ -609,7 +635,7 @@ func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { } // udp service <- frpc <- frps <- frpc visitor <- user - workConnReaderFn := func(conn net.Conn, readCh chan *msg.UdpPacket) { + workConnReaderFn := func(conn net.Conn, readCh chan *msg.UDPPacket) { defer closeFn() for { @@ -621,7 +647,7 @@ func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { default: } - var udpMsg msg.UdpPacket + var udpMsg msg.UDPPacket if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil { xl.Warn("read from workConn for sudp error: %v", errRet) return @@ -646,7 +672,7 @@ func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { var errRet error for rawMsg := range sendCh { switch m := rawMsg.(type) { - case *msg.UdpPacket: + case *msg.UDPPacket: xl.Trace("frpc send udp package to frpc visitor, [udp local: %v, remote: %v], [tcp work conn local: %v, remote: %v]", m.LocalAddr.String(), m.RemoteAddr.String(), conn.LocalAddr().String(), conn.RemoteAddr().String()) case *msg.Ping: @@ -688,11 +714,11 @@ func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { go workConnReaderFn(workConn, readCh) go heartbeatFn(workConn, sendCh) - udp.Forwarder(pxy.localAddr, readCh, sendCh) + udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize)) } // Common handler for tcp work connections. -func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin, +func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin, baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) { xl := xlog.FromContextSafe(ctx) var ( @@ -759,22 +785,22 @@ func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf proxyPlugin.Handle(remote, workConn, extraInfo) xl.Debug("handle by plugin finished") return - } else { - localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort)) - if err != nil { - workConn.Close() - xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err) - return - } - - xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(), - localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String()) - - if len(extraInfo) > 0 { - localConn.Write(extraInfo) - } - - frpIo.Join(localConn, remote) - xl.Debug("join connections closed") } + + localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort)) + if err != nil { + workConn.Close() + xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err) + return + } + + xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(), + localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String()) + + if len(extraInfo) > 0 { + localConn.Write(extraInfo) + } + + frpIo.Join(localConn, remote) + xl.Debug("join connections closed") } diff --git a/client/proxy/proxy_manager.go b/client/proxy/proxy_manager.go index a0afd70a..9a78f0f0 100644 --- a/client/proxy/proxy_manager.go +++ b/client/proxy/proxy_manager.go @@ -14,9 +14,9 @@ import ( "github.com/fatedier/golib/errors" ) -type ProxyManager struct { +type Manager struct { sendCh chan (msg.Message) - proxies map[string]*ProxyWrapper + proxies map[string]*Wrapper closed bool mu sync.RWMutex @@ -29,10 +29,10 @@ type ProxyManager struct { ctx context.Context } -func NewProxyManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager { - return &ProxyManager{ +func NewManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *Manager { + return &Manager{ sendCh: msgSendCh, - proxies: make(map[string]*ProxyWrapper), + proxies: make(map[string]*Wrapper), closed: false, clientCfg: clientCfg, serverUDPPort: serverUDPPort, @@ -40,7 +40,7 @@ func NewProxyManager(ctx context.Context, msgSendCh chan (msg.Message), clientCf } } -func (pm *ProxyManager) StartProxy(name string, remoteAddr string, serverRespErr string) error { +func (pm *Manager) StartProxy(name string, remoteAddr string, serverRespErr string) error { pm.mu.RLock() pxy, ok := pm.proxies[name] pm.mu.RUnlock() @@ -55,16 +55,16 @@ func (pm *ProxyManager) StartProxy(name string, remoteAddr string, serverRespErr return nil } -func (pm *ProxyManager) Close() { +func (pm *Manager) Close() { pm.mu.Lock() defer pm.mu.Unlock() for _, pxy := range pm.proxies { pxy.Stop() } - pm.proxies = make(map[string]*ProxyWrapper) + pm.proxies = make(map[string]*Wrapper) } -func (pm *ProxyManager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) { +func (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) { pm.mu.RLock() pw, ok := pm.proxies[name] pm.mu.RUnlock() @@ -75,7 +75,7 @@ func (pm *ProxyManager) HandleWorkConn(name string, workConn net.Conn, m *msg.St } } -func (pm *ProxyManager) HandleEvent(evType event.EventType, payload interface{}) error { +func (pm *Manager) HandleEvent(evType event.Type, payload interface{}) error { var m msg.Message switch e := payload.(type) { case *event.StartProxyPayload: @@ -92,8 +92,8 @@ func (pm *ProxyManager) HandleEvent(evType event.EventType, payload interface{}) return err } -func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus { - ps := make([]*ProxyStatus, 0) +func (pm *Manager) GetAllProxyStatus() []*WorkingStatus { + ps := make([]*WorkingStatus, 0) pm.mu.RLock() defer pm.mu.RUnlock() for _, pxy := range pm.proxies { @@ -102,7 +102,7 @@ func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus { return ps } -func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) { +func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) { xl := xlog.FromContextSafe(pm.ctx) pm.mu.Lock() defer pm.mu.Unlock() @@ -133,7 +133,7 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) { addPxyNames := make([]string, 0) for name, cfg := range pxyCfgs { if _, ok := pm.proxies[name]; !ok { - pxy := NewProxyWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort) + pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort) pm.proxies[name] = pxy addPxyNames = append(addPxyNames, name) diff --git a/client/proxy/proxy_wrapper.go b/client/proxy/proxy_wrapper.go index 418ef01b..b3b375e8 100644 --- a/client/proxy/proxy_wrapper.go +++ b/client/proxy/proxy_wrapper.go @@ -18,12 +18,12 @@ import ( ) const ( - ProxyStatusNew = "new" - ProxyStatusWaitStart = "wait start" - ProxyStatusStartErr = "start error" - ProxyStatusRunning = "running" - ProxyStatusCheckFailed = "check failed" - ProxyStatusClosed = "closed" + ProxyPhaseNew = "new" + ProxyPhaseWaitStart = "wait start" + ProxyPhaseStartErr = "start error" + ProxyPhaseRunning = "running" + ProxyPhaseCheckFailed = "check failed" + ProxyPhaseClosed = "closed" ) var ( @@ -32,29 +32,29 @@ var ( startErrTimeout = 30 * time.Second ) -type ProxyStatus struct { - Name string `json:"name"` - Type string `json:"type"` - Status string `json:"status"` - Err string `json:"err"` - Cfg config.ProxyConf `json:"cfg"` +type WorkingStatus struct { + Name string `json:"name"` + Type string `json:"type"` + Phase string `json:"status"` + Err string `json:"err"` + Cfg config.ProxyConf `json:"cfg"` // Got from server. RemoteAddr string `json:"remote_addr"` } -type ProxyWrapper struct { - ProxyStatus +type Wrapper struct { + WorkingStatus // underlying proxy pxy Proxy // if ProxyConf has healcheck config // monitor will watch if it is alive - monitor *health.HealthCheckMonitor + monitor *health.Monitor // event handler - handler event.EventHandler + handler event.Handler health uint32 lastSendStartMsg time.Time @@ -67,15 +67,15 @@ type ProxyWrapper struct { ctx context.Context } -func NewProxyWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, serverUDPPort int) *ProxyWrapper { +func NewWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.Handler, serverUDPPort int) *Wrapper { baseInfo := cfg.GetBaseInfo() xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName) - pw := &ProxyWrapper{ - ProxyStatus: ProxyStatus{ - Name: baseInfo.ProxyName, - Type: baseInfo.ProxyType, - Status: ProxyStatusNew, - Cfg: cfg, + pw := &Wrapper{ + WorkingStatus: WorkingStatus{ + Name: baseInfo.ProxyName, + Type: baseInfo.ProxyType, + Phase: ProxyPhaseNew, + Cfg: cfg, }, closeCh: make(chan struct{}), healthNotifyCh: make(chan struct{}), @@ -86,9 +86,9 @@ func NewProxyWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config if baseInfo.HealthCheckType != "" { pw.health = 1 // means failed - pw.monitor = health.NewHealthCheckMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS, + pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS, baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr, - baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback) + baseInfo.HealthCheckURL, pw.statusNormalCallback, pw.statusFailedCallback) xl.Trace("enable health check monitor") } @@ -96,16 +96,16 @@ func NewProxyWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config return pw } -func (pw *ProxyWrapper) SetRunningStatus(remoteAddr string, respErr string) error { +func (pw *Wrapper) SetRunningStatus(remoteAddr string, respErr string) error { pw.mu.Lock() defer pw.mu.Unlock() - if pw.Status != ProxyStatusWaitStart { + if pw.Phase != ProxyPhaseWaitStart { return fmt.Errorf("status not wait start, ignore start message") } pw.RemoteAddr = remoteAddr if respErr != "" { - pw.Status = ProxyStatusStartErr + pw.Phase = ProxyPhaseStartErr pw.Err = respErr pw.lastStartErr = time.Now() return fmt.Errorf(pw.Err) @@ -113,25 +113,25 @@ func (pw *ProxyWrapper) SetRunningStatus(remoteAddr string, respErr string) erro if err := pw.pxy.Run(); err != nil { pw.close() - pw.Status = ProxyStatusStartErr + pw.Phase = ProxyPhaseStartErr pw.Err = err.Error() pw.lastStartErr = time.Now() return err } - pw.Status = ProxyStatusRunning + pw.Phase = ProxyPhaseRunning pw.Err = "" return nil } -func (pw *ProxyWrapper) Start() { +func (pw *Wrapper) Start() { go pw.checkWorker() if pw.monitor != nil { go pw.monitor.Start() } } -func (pw *ProxyWrapper) Stop() { +func (pw *Wrapper) Stop() { pw.mu.Lock() defer pw.mu.Unlock() close(pw.closeCh) @@ -140,11 +140,11 @@ func (pw *ProxyWrapper) Stop() { if pw.monitor != nil { pw.monitor.Stop() } - pw.Status = ProxyStatusClosed + pw.Phase = ProxyPhaseClosed pw.close() } -func (pw *ProxyWrapper) close() { +func (pw *Wrapper) close() { pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{ CloseProxyMsg: &msg.CloseProxy{ ProxyName: pw.Name, @@ -152,7 +152,7 @@ func (pw *ProxyWrapper) close() { }) } -func (pw *ProxyWrapper) checkWorker() { +func (pw *Wrapper) checkWorker() { xl := pw.xl if pw.monitor != nil { // let monitor do check request first @@ -163,13 +163,13 @@ func (pw *ProxyWrapper) checkWorker() { now := time.Now() if atomic.LoadUint32(&pw.health) == 0 { pw.mu.Lock() - if pw.Status == ProxyStatusNew || - pw.Status == ProxyStatusCheckFailed || - (pw.Status == ProxyStatusWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) || - (pw.Status == ProxyStatusStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) { + if pw.Phase == ProxyPhaseNew || + pw.Phase == ProxyPhaseCheckFailed || + (pw.Phase == ProxyPhaseWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) || + (pw.Phase == ProxyPhaseStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) { - xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart) - pw.Status = ProxyStatusWaitStart + xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseWaitStart) + pw.Phase = ProxyPhaseWaitStart var newProxyMsg msg.NewProxy pw.Cfg.MarshalToMsg(&newProxyMsg) @@ -181,10 +181,10 @@ func (pw *ProxyWrapper) checkWorker() { pw.mu.Unlock() } else { pw.mu.Lock() - if pw.Status == ProxyStatusRunning || pw.Status == ProxyStatusWaitStart { + if pw.Phase == ProxyPhaseRunning || pw.Phase == ProxyPhaseWaitStart { pw.close() - xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed) - pw.Status = ProxyStatusCheckFailed + xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseCheckFailed) + pw.Phase = ProxyPhaseCheckFailed } pw.mu.Unlock() } @@ -198,7 +198,7 @@ func (pw *ProxyWrapper) checkWorker() { } } -func (pw *ProxyWrapper) statusNormalCallback() { +func (pw *Wrapper) statusNormalCallback() { xl := pw.xl atomic.StoreUint32(&pw.health, 0) errors.PanicToError(func() { @@ -210,7 +210,7 @@ func (pw *ProxyWrapper) statusNormalCallback() { xl.Info("health check success") } -func (pw *ProxyWrapper) statusFailedCallback() { +func (pw *Wrapper) statusFailedCallback() { xl := pw.xl atomic.StoreUint32(&pw.health, 1) errors.PanicToError(func() { @@ -222,7 +222,7 @@ func (pw *ProxyWrapper) statusFailedCallback() { xl.Info("health check failed") } -func (pw *ProxyWrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) { +func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) { xl := pw.xl pw.mu.RLock() pxy := pw.pxy @@ -235,13 +235,13 @@ func (pw *ProxyWrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) { } } -func (pw *ProxyWrapper) GetStatus() *ProxyStatus { +func (pw *Wrapper) GetStatus() *WorkingStatus { pw.mu.RLock() defer pw.mu.RUnlock() - ps := &ProxyStatus{ + ps := &WorkingStatus{ Name: pw.Name, Type: pw.Type, - Status: pw.Status, + Phase: pw.Phase, Err: pw.Err, Cfg: pw.Cfg, RemoteAddr: pw.RemoteAddr, diff --git a/client/service.go b/client/service.go index b6ea0634..e6451ff1 100644 --- a/client/service.go +++ b/client/service.go @@ -18,6 +18,7 @@ import ( "context" "crypto/tls" "fmt" + "github.com/fatedier/frp/models/transport" "io/ioutil" "net" "runtime" @@ -40,7 +41,7 @@ import ( // Service is a client service. type Service struct { // uniq id got from frps, attach it in loginMsg - runId string + runID string // manager control connection with server ctl *Control @@ -73,7 +74,7 @@ func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf ctx, cancel := context.WithCancel(context.Background()) svr = &Service{ - authSetter: auth.NewAuthSetter(cfg.AuthClientConfig), + authSetter: auth.NewAuthSetter(cfg.ClientConfig), cfg: cfg, cfgFile: cfgFile, pxyCfgs: pxyCfgs, @@ -104,12 +105,11 @@ func (svr *Service) Run() error { // otherwise sleep a while and try again to connect to server if svr.cfg.LoginFailExit { return err - } else { - time.Sleep(10 * time.Second) } + time.Sleep(10 * time.Second) } else { // login success - ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) + ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) ctl.Run() svr.ctlMu.Lock() svr.ctl = ctl @@ -185,7 +185,7 @@ func (svr *Service) keepControllerWorking() { // reconnect success, init delayTime delayTime = time.Second - ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) + ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) ctl.Run() svr.ctlMu.Lock() if svr.ctl != nil { @@ -205,11 +205,17 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { xl := xlog.FromContextSafe(svr.ctx) var tlsConfig *tls.Config if svr.cfg.TLSEnable { - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, + tlsConfig, err = transport.NewClientTLSConfig( + svr.cfg.TLSCertFile, + svr.cfg.TLSKeyFile, + svr.cfg.TLSTrustedCaFile, + svr.cfg.ServerAddr) + if err != nil { + xl.Warn("fail to build tls configuration when service login, err: %v", err) + return } } - conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HttpProxy, svr.cfg.Protocol, + conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, fmt.Sprintf("%s:%d", svr.cfg.ServerAddr, svr.cfg.ServerPort), tlsConfig) if err != nil { return @@ -224,7 +230,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { } }() - if svr.cfg.TcpMux { + if svr.cfg.TCPMux { fmuxCfg := fmux.DefaultConfig() fmuxCfg.KeepAliveInterval = 20 * time.Second fmuxCfg.LogOutput = ioutil.Discard @@ -248,7 +254,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { User: svr.cfg.User, Version: version.Full(), Timestamp: time.Now().Unix(), - RunId: svr.runId, + RunID: svr.runID, Metas: svr.cfg.Metas, } @@ -274,12 +280,12 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { return } - svr.runId = loginRespMsg.RunId + svr.runID = loginRespMsg.RunID xl.ResetPrefixes() - xl.AppendPrefix(svr.runId) + xl.AppendPrefix(svr.runID) - svr.serverUDPPort = loginRespMsg.ServerUdpPort - xl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort) + svr.serverUDPPort = loginRespMsg.ServerUDPPort + xl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunID, loginRespMsg.ServerUDPPort) return } @@ -294,6 +300,8 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs func (svr *Service) Close() { atomic.StoreUint32(&svr.exit, 1) - svr.ctl.Close() + if svr.ctl != nil { + svr.ctl.Close() + } svr.cancel() } diff --git a/client/visitor.go b/client/visitor.go index d3004868..ce5625f0 100644 --- a/client/visitor.go +++ b/client/visitor.go @@ -50,18 +50,18 @@ func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visi ctx: xlog.NewContext(ctx, xl), } switch cfg := cfg.(type) { - case *config.StcpVisitorConf: - visitor = &StcpVisitor{ + case *config.STCPVisitorConf: + visitor = &STCPVisitor{ BaseVisitor: &baseVisitor, cfg: cfg, } - case *config.XtcpVisitorConf: - visitor = &XtcpVisitor{ + case *config.XTCPVisitorConf: + visitor = &XTCPVisitor{ BaseVisitor: &baseVisitor, cfg: cfg, } - case *config.SudpVisitorConf: - visitor = &SudpVisitor{ + case *config.SUDPVisitorConf: + visitor = &SUDPVisitor{ BaseVisitor: &baseVisitor, cfg: cfg, checkCloseCh: make(chan struct{}), @@ -79,13 +79,13 @@ type BaseVisitor struct { ctx context.Context } -type StcpVisitor struct { +type STCPVisitor struct { *BaseVisitor - cfg *config.StcpVisitorConf + cfg *config.STCPVisitorConf } -func (sv *StcpVisitor) Run() (err error) { +func (sv *STCPVisitor) Run() (err error) { sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) if err != nil { return @@ -95,11 +95,11 @@ func (sv *StcpVisitor) Run() (err error) { return } -func (sv *StcpVisitor) Close() { +func (sv *STCPVisitor) Close() { sv.l.Close() } -func (sv *StcpVisitor) worker() { +func (sv *STCPVisitor) worker() { xl := xlog.FromContextSafe(sv.ctx) for { conn, err := sv.l.Accept() @@ -112,7 +112,7 @@ func (sv *StcpVisitor) worker() { } } -func (sv *StcpVisitor) handleConn(userConn net.Conn) { +func (sv *STCPVisitor) handleConn(userConn net.Conn) { xl := xlog.FromContextSafe(sv.ctx) defer userConn.Close() @@ -168,13 +168,13 @@ func (sv *StcpVisitor) handleConn(userConn net.Conn) { frpIo.Join(userConn, remote) } -type XtcpVisitor struct { +type XTCPVisitor struct { *BaseVisitor - cfg *config.XtcpVisitorConf + cfg *config.XTCPVisitorConf } -func (sv *XtcpVisitor) Run() (err error) { +func (sv *XTCPVisitor) Run() (err error) { sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) if err != nil { return @@ -184,11 +184,11 @@ func (sv *XtcpVisitor) Run() (err error) { return } -func (sv *XtcpVisitor) Close() { +func (sv *XTCPVisitor) Close() { sv.l.Close() } -func (sv *XtcpVisitor) worker() { +func (sv *XTCPVisitor) worker() { xl := xlog.FromContextSafe(sv.ctx) for { conn, err := sv.l.Accept() @@ -201,7 +201,7 @@ func (sv *XtcpVisitor) worker() { } } -func (sv *XtcpVisitor) handleConn(userConn net.Conn) { +func (sv *XTCPVisitor) handleConn(userConn net.Conn) { xl := xlog.FromContextSafe(sv.ctx) defer userConn.Close() @@ -300,7 +300,7 @@ func (sv *XtcpVisitor) handleConn(userConn net.Conn) { // wrap kcp connection var remote io.ReadWriteCloser - remote, err = frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.ClientAddr) + remote, err = frpNet.NewKCPConnFromUDP(lConn, true, natHoleRespMsg.ClientAddr) if err != nil { xl.Error("create kcp connection from udp connection error: %v", err) return @@ -337,20 +337,20 @@ func (sv *XtcpVisitor) handleConn(userConn net.Conn) { xl.Debug("join connections closed") } -type SudpVisitor struct { +type SUDPVisitor struct { *BaseVisitor checkCloseCh chan struct{} // udpConn is the listener of udp packet udpConn *net.UDPConn - readCh chan *msg.UdpPacket - sendCh chan *msg.UdpPacket + readCh chan *msg.UDPPacket + sendCh chan *msg.UDPPacket - cfg *config.SudpVisitorConf + cfg *config.SUDPVisitorConf } // SUDP Run start listen a udp port -func (sv *SudpVisitor) Run() (err error) { +func (sv *SUDPVisitor) Run() (err error) { xl := xlog.FromContextSafe(sv.ctx) addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) @@ -363,18 +363,18 @@ func (sv *SudpVisitor) Run() (err error) { return fmt.Errorf("listen udp port %s error: %v", addr.String(), err) } - sv.sendCh = make(chan *msg.UdpPacket, 1024) - sv.readCh = make(chan *msg.UdpPacket, 1024) + sv.sendCh = make(chan *msg.UDPPacket, 1024) + sv.readCh = make(chan *msg.UDPPacket, 1024) xl.Info("sudp start to work") go sv.dispatcher() - go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh) + go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh, int(sv.ctl.clientCfg.UDPPacketSize)) return } -func (sv *SudpVisitor) dispatcher() { +func (sv *SUDPVisitor) dispatcher() { xl := xlog.FromContextSafe(sv.ctx) for { @@ -409,7 +409,7 @@ func (sv *SudpVisitor) dispatcher() { } } -func (sv *SudpVisitor) worker(workConn net.Conn) { +func (sv *SUDPVisitor) worker(workConn net.Conn) { xl := xlog.FromContextSafe(sv.ctx) xl.Debug("starting sudp proxy worker") @@ -443,7 +443,7 @@ func (sv *SudpVisitor) worker(workConn net.Conn) { case *msg.Ping: xl.Debug("frpc visitor get ping message from frpc") continue - case *msg.UdpPacket: + case *msg.UDPPacket: if errRet := errors.PanicToError(func() { sv.readCh <- m xl.Trace("frpc visitor get udp packet from frpc") @@ -488,8 +488,9 @@ func (sv *SudpVisitor) worker(workConn net.Conn) { xl.Info("sudp worker is closed") } -func (sv *SudpVisitor) getNewVisitorConn() (visitorConn net.Conn, err error) { - visitorConn, err = sv.ctl.connectServer() +func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) { + xl := xlog.FromContextSafe(sv.ctx) + visitorConn, err := sv.ctl.connectServer() if err != nil { return nil, fmt.Errorf("frpc connect frps error: %v", err) } @@ -518,10 +519,23 @@ func (sv *SudpVisitor) getNewVisitorConn() (visitorConn net.Conn, err error) { if newVisitorConnRespMsg.Error != "" { return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error) } - return + + var remote io.ReadWriteCloser + remote = visitorConn + if sv.cfg.UseEncryption { + remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk)) + if err != nil { + xl.Error("create encryption stream error: %v", err) + return nil, err + } + } + if sv.cfg.UseCompression { + remote = frpIo.WithCompression(remote) + } + return frpNet.WrapReadWriteCloserToConn(remote, visitorConn), nil } -func (sv *SudpVisitor) Close() { +func (sv *SUDPVisitor) Close() { sv.mu.Lock() defer sv.mu.Unlock() diff --git a/cmd/frpc/sub/http.go b/cmd/frpc/sub/http.go index 081102cb..801ac5b5 100644 --- a/cmd/frpc/sub/http.go +++ b/cmd/frpc/sub/http.go @@ -26,17 +26,10 @@ import ( ) func init() { - httpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - httpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - httpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - httpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - httpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(httpCmd) httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") - httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + httpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") httpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") httpCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain") httpCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain") @@ -60,20 +53,20 @@ var httpCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.HttpProxyConf{} + cfg := &config.HTTPProxyConf{} var prefix string if user != "" { prefix = user + "." } cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.HttpProxy - cfg.LocalIp = localIp + cfg.ProxyType = consts.HTTPProxy + cfg.LocalIP = localIP cfg.LocalPort = localPort cfg.CustomDomains = strings.Split(customDomains, ",") cfg.SubDomain = subDomain cfg.Locations = strings.Split(locations, ",") - cfg.HttpUser = httpUser - cfg.HttpPwd = httpPwd + cfg.HTTPUser = httpUser + cfg.HTTPPwd = httpPwd cfg.HostHeaderRewrite = hostHeaderRewrite cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression diff --git a/cmd/frpc/sub/https.go b/cmd/frpc/sub/https.go index 7d5fbe8b..0c01f558 100644 --- a/cmd/frpc/sub/https.go +++ b/cmd/frpc/sub/https.go @@ -26,17 +26,10 @@ import ( ) func init() { - httpsCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - httpsCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - httpsCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - httpsCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - httpsCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(httpsCmd) httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") - httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + httpsCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") httpsCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") httpsCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain") httpsCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain") @@ -56,14 +49,14 @@ var httpsCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.HttpsProxyConf{} + cfg := &config.HTTPSProxyConf{} var prefix string if user != "" { prefix = user + "." } cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.HttpsProxy - cfg.LocalIp = localIp + cfg.ProxyType = consts.HTTPSProxy + cfg.LocalIP = localIP cfg.LocalPort = localPort cfg.CustomDomains = strings.Split(customDomains, ",") cfg.SubDomain = subDomain diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index 8f47986a..44904768 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -53,7 +53,7 @@ var ( disableLogColor bool proxyName string - localIp string + localIP string localPort int remotePort int useEncryption bool @@ -71,6 +71,8 @@ var ( bindAddr string bindPort int + tlsEnable bool + kcpDoneCh chan struct{} ) @@ -81,6 +83,18 @@ func init() { kcpDoneCh = make(chan struct{}) } +func RegisterCommonFlags(cmd *cobra.Command) { + cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") + cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") + cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") + cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") + cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") + cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") + cmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") + cmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + cmd.PersistentFlags().BoolVarP(&tlsEnable, "tls_enable", "", false, "enable frpc tls") +} + var rootCmd = &cobra.Command{ Use: "frpc", Short: "frpc is the client of frp (https://github.com/fatedier/frp)", @@ -170,8 +184,9 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) { cfg.DisableLogColor = disableLogColor // Only token authentication is supported in cmd mode - cfg.AuthClientConfig = auth.GetDefaultAuthClientConf() + cfg.ClientConfig = auth.GetDefaultClientConf() cfg.Token = token + cfg.TLSEnable = tlsEnable return } @@ -197,12 +212,18 @@ func runClient(cfgFilePath string) (err error) { return } -func startService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (err error) { +func startService( + cfg config.ClientCommonConf, + pxyCfgs map[string]config.ProxyConf, + visitorCfgs map[string]config.VisitorConf, + cfgFile string, +) (err error) { + log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor) - if cfg.DnsServer != "" { - s := cfg.DnsServer + if cfg.DNSServer != "" { + s := cfg.DNSServer if !strings.Contains(s, ":") { s += ":53" } diff --git a/cmd/frpc/sub/status.go b/cmd/frpc/sub/status.go index 783ec29f..a9123f9f 100644 --- a/cmd/frpc/sub/status.go +++ b/cmd/frpc/sub/status.go @@ -95,55 +95,55 @@ func status(clientCfg config.ClientCommonConf) error { } fmt.Println("Proxy Status...") - if len(res.Tcp) > 0 { + if len(res.TCP) > 0 { fmt.Printf("TCP") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Tcp { + for _, ps := range res.TCP { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() fmt.Println("") } - if len(res.Udp) > 0 { + if len(res.UDP) > 0 { fmt.Printf("UDP") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Udp { + for _, ps := range res.UDP { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() fmt.Println("") } - if len(res.Http) > 0 { + if len(res.HTTP) > 0 { fmt.Printf("HTTP") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Http { + for _, ps := range res.HTTP { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() fmt.Println("") } - if len(res.Https) > 0 { + if len(res.HTTPS) > 0 { fmt.Printf("HTTPS") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Https { + for _, ps := range res.HTTPS { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() fmt.Println("") } - if len(res.Stcp) > 0 { + if len(res.STCP) > 0 { fmt.Printf("STCP") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Stcp { + for _, ps := range res.STCP { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() fmt.Println("") } - if len(res.Xtcp) > 0 { + if len(res.XTCP) > 0 { fmt.Printf("XTCP") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.Xtcp { + for _, ps := range res.XTCP { tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) } tbl.Print() diff --git a/cmd/frpc/sub/stcp.go b/cmd/frpc/sub/stcp.go index 1a04fbf3..24e881d4 100644 --- a/cmd/frpc/sub/stcp.go +++ b/cmd/frpc/sub/stcp.go @@ -25,20 +25,13 @@ import ( ) func init() { - stcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - stcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - stcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - stcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - stcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(stcpCmd) stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") stcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key") stcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name") - stcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + stcpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") stcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") stcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr") stcpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port") @@ -67,14 +60,14 @@ var stcpCmd = &cobra.Command{ } if role == "server" { - cfg := &config.StcpProxyConf{} + cfg := &config.STCPProxyConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.StcpProxy + cfg.ProxyType = consts.STCPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role cfg.Sk = sk - cfg.LocalIp = localIp + cfg.LocalIP = localIP cfg.LocalPort = localPort err = cfg.CheckForCli() if err != nil { @@ -83,9 +76,9 @@ var stcpCmd = &cobra.Command{ } proxyConfs[cfg.ProxyName] = cfg } else if role == "visitor" { - cfg := &config.StcpVisitorConf{} + cfg := &config.STCPVisitorConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.StcpProxy + cfg.ProxyType = consts.STCPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role diff --git a/cmd/frpc/sub/sudp.go b/cmd/frpc/sub/sudp.go index e3e91abc..399a5627 100644 --- a/cmd/frpc/sub/sudp.go +++ b/cmd/frpc/sub/sudp.go @@ -25,20 +25,13 @@ import ( ) func init() { - sudpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - sudpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - sudpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - sudpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - sudpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - sudpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - sudpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - sudpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(sudpCmd) sudpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") sudpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") sudpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key") sudpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name") - sudpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + sudpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") sudpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") sudpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr") sudpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port") @@ -67,14 +60,14 @@ var sudpCmd = &cobra.Command{ } if role == "server" { - cfg := &config.SudpProxyConf{} + cfg := &config.SUDPProxyConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.SudpProxy + cfg.ProxyType = consts.SUDPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role cfg.Sk = sk - cfg.LocalIp = localIp + cfg.LocalIP = localIP cfg.LocalPort = localPort err = cfg.CheckForCli() if err != nil { @@ -83,9 +76,9 @@ var sudpCmd = &cobra.Command{ } proxyConfs[cfg.ProxyName] = cfg } else if role == "visitor" { - cfg := &config.SudpVisitorConf{} + cfg := &config.SUDPVisitorConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.SudpProxy + cfg.ProxyType = consts.SUDPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role diff --git a/cmd/frpc/sub/tcp.go b/cmd/frpc/sub/tcp.go index c2ccd2b8..ee30669c 100644 --- a/cmd/frpc/sub/tcp.go +++ b/cmd/frpc/sub/tcp.go @@ -25,17 +25,10 @@ import ( ) func init() { - tcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - tcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - tcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp") - tcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - tcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(tcpCmd) tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") - tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + tcpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") tcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") tcpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port") tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") @@ -54,14 +47,14 @@ var tcpCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.TcpProxyConf{} + cfg := &config.TCPProxyConf{} var prefix string if user != "" { prefix = user + "." } cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.TcpProxy - cfg.LocalIp = localIp + cfg.ProxyType = consts.TCPProxy + cfg.LocalIP = localIP cfg.LocalPort = localPort cfg.RemotePort = remotePort cfg.UseEncryption = useEncryption diff --git a/cmd/frpc/sub/tcpmux.go b/cmd/frpc/sub/tcpmux.go index d67d4981..7a150949 100644 --- a/cmd/frpc/sub/tcpmux.go +++ b/cmd/frpc/sub/tcpmux.go @@ -26,17 +26,10 @@ import ( ) func init() { - tcpMuxCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - tcpMuxCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - tcpMuxCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - tcpMuxCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - tcpMuxCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - tcpMuxCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - tcpMuxCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - tcpMuxCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(tcpMuxCmd) tcpMuxCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") - tcpMuxCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + tcpMuxCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") tcpMuxCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") tcpMuxCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain") tcpMuxCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain") @@ -57,14 +50,14 @@ var tcpMuxCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.TcpMuxProxyConf{} + cfg := &config.TCPMuxProxyConf{} var prefix string if user != "" { prefix = user + "." } cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.TcpMuxProxy - cfg.LocalIp = localIp + cfg.ProxyType = consts.TCPMuxProxy + cfg.LocalIP = localIP cfg.LocalPort = localPort cfg.CustomDomains = strings.Split(customDomains, ",") cfg.SubDomain = subDomain diff --git a/cmd/frpc/sub/udp.go b/cmd/frpc/sub/udp.go index 0d73c763..01ff466c 100644 --- a/cmd/frpc/sub/udp.go +++ b/cmd/frpc/sub/udp.go @@ -25,17 +25,10 @@ import ( ) func init() { - udpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - udpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - udpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - udpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - udpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(udpCmd) udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") - udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + udpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") udpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") udpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port") udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") @@ -54,14 +47,14 @@ var udpCmd = &cobra.Command{ os.Exit(1) } - cfg := &config.UdpProxyConf{} + cfg := &config.UDPProxyConf{} var prefix string if user != "" { prefix = user + "." } cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.UdpProxy - cfg.LocalIp = localIp + cfg.ProxyType = consts.UDPProxy + cfg.LocalIP = localIP cfg.LocalPort = localPort cfg.RemotePort = remotePort cfg.UseEncryption = useEncryption diff --git a/cmd/frpc/sub/xtcp.go b/cmd/frpc/sub/xtcp.go index 558294da..018cbf36 100644 --- a/cmd/frpc/sub/xtcp.go +++ b/cmd/frpc/sub/xtcp.go @@ -25,20 +25,13 @@ import ( ) func init() { - xtcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") - xtcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") - xtcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") - xtcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") - xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") - xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") - xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") - xtcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") + RegisterCommonFlags(xtcpCmd) xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") xtcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key") xtcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name") - xtcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") + xtcpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip") xtcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port") xtcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr") xtcpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port") @@ -67,14 +60,14 @@ var xtcpCmd = &cobra.Command{ } if role == "server" { - cfg := &config.XtcpProxyConf{} + cfg := &config.XTCPProxyConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.XtcpProxy + cfg.ProxyType = consts.XTCPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role cfg.Sk = sk - cfg.LocalIp = localIp + cfg.LocalIP = localIP cfg.LocalPort = localPort err = cfg.CheckForCli() if err != nil { @@ -83,9 +76,9 @@ var xtcpCmd = &cobra.Command{ } proxyConfs[cfg.ProxyName] = cfg } else if role == "visitor" { - cfg := &config.XtcpVisitorConf{} + cfg := &config.XTCPVisitorConf{} cfg.ProxyName = prefix + proxyName - cfg.ProxyType = consts.XtcpProxy + cfg.ProxyType = consts.XTCPProxy cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression cfg.Role = role diff --git a/cmd/frps/root.go b/cmd/frps/root.go index 9096b28c..b6fb362a 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -39,12 +39,12 @@ var ( bindAddr string bindPort int - bindUdpPort int + bindUDPPort int kcpBindPort int proxyBindAddr string - vhostHttpPort int - vhostHttpsPort int - vhostHttpTimeout int64 + vhostHTTPPort int + vhostHTTPSPort int + vhostHTTPTimeout int64 dashboardAddr string dashboardPort int dashboardUser string @@ -60,20 +60,21 @@ var ( allowPorts string maxPoolCount int64 maxPortsPerClient int64 + tlsOnly bool ) func init() { rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file of frps") - rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") + rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps") rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "0.0.0.0", "bind address") rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port") - rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port") + rootCmd.PersistentFlags().IntVarP(&bindUDPPort, "bind_udp_port", "", 0, "bind udp port") rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port") rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address") - rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port") - rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port") - rootCmd.PersistentFlags().Int64VarP(&vhostHttpTimeout, "vhost_http_timeout", "", 60, "vhost http response header timeout") + rootCmd.PersistentFlags().IntVarP(&vhostHTTPPort, "vhost_http_port", "", 0, "vhost http port") + rootCmd.PersistentFlags().IntVarP(&vhostHTTPSPort, "vhost_https_port", "", 0, "vhost https port") + rootCmd.PersistentFlags().Int64VarP(&vhostHTTPTimeout, "vhost_http_timeout", "", 60, "vhost http response header timeout") rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address") rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port") rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user") @@ -87,6 +88,7 @@ func init() { rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host") rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports") rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client") + rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only") } var rootCmd = &cobra.Command{ @@ -159,12 +161,12 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) { cfg.BindAddr = bindAddr cfg.BindPort = bindPort - cfg.BindUdpPort = bindUdpPort - cfg.KcpBindPort = kcpBindPort + cfg.BindUDPPort = bindUDPPort + cfg.KCPBindPort = kcpBindPort cfg.ProxyBindAddr = proxyBindAddr - cfg.VhostHttpPort = vhostHttpPort - cfg.VhostHttpsPort = vhostHttpsPort - cfg.VhostHttpTimeout = vhostHttpTimeout + cfg.VhostHTTPPort = vhostHTTPPort + cfg.VhostHTTPSPort = vhostHTTPSPort + cfg.VhostHTTPTimeout = vhostHTTPTimeout cfg.DashboardAddr = dashboardAddr cfg.DashboardPort = dashboardPort cfg.DashboardUser = dashboardUser @@ -173,9 +175,10 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) { cfg.LogLevel = logLevel cfg.LogMaxDays = logMaxDays cfg.SubDomainHost = subDomainHost + cfg.TLSOnly = tlsOnly // Only token authentication is supported in cmd mode - cfg.AuthServerConfig = auth.GetDefaultAuthServerConf() + cfg.ServerConfig = auth.GetDefaultServerConf() cfg.Token = token if len(allowPorts) > 0 { // e.g. 1000-2000,2001,2002,3000-4000 diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 35fbb171..f47c1f21 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -52,6 +52,10 @@ protocol = tcp # if tls_enable is true, frpc will connect frps by tls tls_enable = true +# tls_cert_file = client.crt +# tls_key_file = client.key +# tls_trusted_ca_file = ca.crt + # specify a dns server, so frpc will use this instead of default one # dns_server = 8.8.8.8 @@ -68,6 +72,11 @@ tls_enable = true meta_var1 = 123 meta_var2 = 234 +# specify udp packet size, unit is byte. If not set, the default value is 1500. +# This parameter should be same between client and server. +# It affects the udp and sudp proxy. +udp_packet_size = 1500 + # 'ssh' is the unique proxy name # if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh' [ssh] diff --git a/conf/frps_full.ini b/conf/frps_full.ini index 03896a2d..969bbe2d 100644 --- a/conf/frps_full.ini +++ b/conf/frps_full.ini @@ -103,6 +103,10 @@ max_ports_per_client = 0 # TlsOnly specifies whether to only accept TLS-encrypted connections. By default, the value is false. tls_only = false +# tls_cert_file = server.crt +# tls_key_file = server.key +# tls_trusted_ca_file = ca.crt + # if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file # when subdomain is test, the host used by routing is test.frps.com subdomain_host = frps.com @@ -113,6 +117,11 @@ tcp_mux = true # custom 404 page for HTTP requests # custom_404_page = /path/to/404.html +# specify udp packet size, unit is byte. If not set, the default value is 1500. +# This parameter should be same between client and server. +# It affects the udp and sudp proxy. +udp_packet_size = 1500 + [plugin.user-manager] addr = 127.0.0.1:9000 path = /handler diff --git a/go.mod b/go.mod index c511e8c2..d681988a 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible github.com/golang/snappy v0.0.0-20170215233205-553a64147049 // indirect + github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.0 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d @@ -16,22 +17,24 @@ require ( github.com/klauspost/cpuid v1.2.0 // indirect github.com/klauspost/reedsolomon v1.9.1 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect + github.com/onsi/ginkgo v1.12.3 + github.com/onsi/gomega v1.10.1 github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v1.4.1 github.com/rakyll/statik v0.1.1 github.com/rodaine/table v1.0.0 github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.1 // indirect github.com/stretchr/testify v1.4.0 github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect - golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 + golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d - golang.org/x/text v0.3.2 // indirect + golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 gopkg.in/square/go-jose.v2 v2.4.1 // indirect + k8s.io/apimachinery v0.18.3 ) diff --git a/go.sum b/go.sum index 94a7f930..2b5e4275 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -16,39 +19,73 @@ github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk= github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8= github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8= @@ -60,6 +97,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -68,7 +106,22 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ= +github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -101,8 +154,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -122,41 +176,84 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk= +k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/hack/run-e2e.sh b/hack/run-e2e.sh new file mode 100755 index 00000000..68db6bfa --- /dev/null +++ b/hack/run-e2e.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) + +which ginkgo &> /dev/null +if [ $? -ne 0 ]; then + echo "ginkgo not found, try to install..." + go get -u github.com/onsi/ginkgo/ginkgo +fi + +debug=false +if [ x${DEBUG} == x"true" ]; then + debug=true +fi +ginkgo -nodes=4 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=debug -debug=${debug} diff --git a/models/auth/auth.go b/models/auth/auth.go index 90a0c160..adbcb3d9 100644 --- a/models/auth/auth.go +++ b/models/auth/auth.go @@ -72,42 +72,42 @@ func unmarshalBaseConfFromIni(conf ini.File) baseConfig { return cfg } -type AuthClientConfig struct { +type ClientConfig struct { baseConfig oidcClientConfig tokenConfig } -func GetDefaultAuthClientConf() AuthClientConfig { - return AuthClientConfig{ +func GetDefaultClientConf() ClientConfig { + return ClientConfig{ baseConfig: getDefaultBaseConf(), oidcClientConfig: getDefaultOidcClientConf(), tokenConfig: getDefaultTokenConf(), } } -func UnmarshalAuthClientConfFromIni(conf ini.File) (cfg AuthClientConfig) { +func UnmarshalClientConfFromIni(conf ini.File) (cfg ClientConfig) { cfg.baseConfig = unmarshalBaseConfFromIni(conf) cfg.oidcClientConfig = unmarshalOidcClientConfFromIni(conf) cfg.tokenConfig = unmarshalTokenConfFromIni(conf) return cfg } -type AuthServerConfig struct { +type ServerConfig struct { baseConfig oidcServerConfig tokenConfig } -func GetDefaultAuthServerConf() AuthServerConfig { - return AuthServerConfig{ +func GetDefaultServerConf() ServerConfig { + return ServerConfig{ baseConfig: getDefaultBaseConf(), oidcServerConfig: getDefaultOidcServerConf(), tokenConfig: getDefaultTokenConf(), } } -func UnmarshalAuthServerConfFromIni(conf ini.File) (cfg AuthServerConfig) { +func UnmarshalServerConfFromIni(conf ini.File) (cfg ServerConfig) { cfg.baseConfig = unmarshalBaseConfFromIni(conf) cfg.oidcServerConfig = unmarshalOidcServerConfFromIni(conf) cfg.tokenConfig = unmarshalTokenConfFromIni(conf) @@ -120,7 +120,7 @@ type Setter interface { SetNewWorkConn(*msg.NewWorkConn) error } -func NewAuthSetter(cfg AuthClientConfig) (authProvider Setter) { +func NewAuthSetter(cfg ClientConfig) (authProvider Setter) { switch cfg.AuthenticationMethod { case consts.TokenAuthMethod: authProvider = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig) @@ -139,7 +139,7 @@ type Verifier interface { VerifyNewWorkConn(*msg.NewWorkConn) error } -func NewAuthVerifier(cfg AuthServerConfig) (authVerifier Verifier) { +func NewAuthVerifier(cfg ServerConfig) (authVerifier Verifier) { switch cfg.AuthenticationMethod { case consts.TokenAuthMethod: authVerifier = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig) diff --git a/models/auth/oidc.go b/models/auth/oidc.go index b38b1c08..91c70be0 100644 --- a/models/auth/oidc.go +++ b/models/auth/oidc.go @@ -26,10 +26,10 @@ import ( ) type oidcClientConfig struct { - // OidcClientId specifies the client ID to use to get a token in OIDC + // OidcClientID specifies the client ID to use to get a token in OIDC // authentication if AuthenticationMethod == "oidc". By default, this value // is "". - OidcClientId string `json:"oidc_client_id"` + OidcClientID string `json:"oidc_client_id"` // OidcClientSecret specifies the client secret to use to get a token in OIDC // authentication if AuthenticationMethod == "oidc". By default, this value // is "". @@ -37,18 +37,18 @@ type oidcClientConfig struct { // OidcAudience specifies the audience of the token in OIDC authentication //if AuthenticationMethod == "oidc". By default, this value is "". OidcAudience string `json:"oidc_audience"` - // OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint. + // OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint. // It will be used to get an OIDC token if AuthenticationMethod == "oidc". // By default, this value is "". - OidcTokenEndpointUrl string `json:"oidc_token_endpoint_url"` + OidcTokenEndpointURL string `json:"oidc_token_endpoint_url"` } func getDefaultOidcClientConf() oidcClientConfig { return oidcClientConfig{ - OidcClientId: "", + OidcClientID: "", OidcClientSecret: "", OidcAudience: "", - OidcTokenEndpointUrl: "", + OidcTokenEndpointURL: "", } } @@ -61,7 +61,7 @@ func unmarshalOidcClientConfFromIni(conf ini.File) oidcClientConfig { cfg := getDefaultOidcClientConf() if tmpStr, ok = conf.Get("common", "oidc_client_id"); ok { - cfg.OidcClientId = tmpStr + cfg.OidcClientID = tmpStr } if tmpStr, ok = conf.Get("common", "oidc_client_secret"); ok { @@ -73,7 +73,7 @@ func unmarshalOidcClientConfFromIni(conf ini.File) oidcClientConfig { } if tmpStr, ok = conf.Get("common", "oidc_token_endpoint_url"); ok { - cfg.OidcTokenEndpointUrl = tmpStr + cfg.OidcTokenEndpointURL = tmpStr } return cfg @@ -148,10 +148,10 @@ type OidcAuthProvider struct { func NewOidcAuthSetter(baseCfg baseConfig, cfg oidcClientConfig) *OidcAuthProvider { tokenGenerator := &clientcredentials.Config{ - ClientID: cfg.OidcClientId, + ClientID: cfg.OidcClientID, ClientSecret: cfg.OidcClientSecret, Scopes: []string{cfg.OidcAudience}, - TokenURL: cfg.OidcTokenEndpointUrl, + TokenURL: cfg.OidcTokenEndpointURL, } return &OidcAuthProvider{ diff --git a/models/config/client_common.go b/models/config/client_common.go index 3f8c485d..1fe148f6 100644 --- a/models/config/client_common.go +++ b/models/config/client_common.go @@ -29,17 +29,17 @@ import ( // recommended to use GetDefaultClientConf instead of creating this object // directly, so that all unspecified fields have reasonable default values. type ClientCommonConf struct { - auth.AuthClientConfig + auth.ClientConfig // ServerAddr specifies the address of the server to connect to. By // default, this value is "0.0.0.0". ServerAddr string `json:"server_addr"` // ServerPort specifies the port to connect to the server on. By default, // this value is 7000. ServerPort int `json:"server_port"` - // HttpProxy specifies a proxy address to connect to the server through. If + // HTTPProxy specifies a proxy address to connect to the server through. If // this value is "", the server will be connected to directly. By default, // this value is read from the "http_proxy" environment variable. - HttpProxy string `json:"http_proxy"` + HTTPProxy string `json:"http_proxy"` // LogFile specifies a file where logs will be written to. This value will // only be used if LogWay is set appropriately. By default, this value is // "console". @@ -79,18 +79,18 @@ type ClientCommonConf struct { // PoolCount specifies the number of connections the client will make to // the server in advance. By default, this value is 0. PoolCount int `json:"pool_count"` - // TcpMux toggles TCP stream multiplexing. This allows multiple requests + // TCPMux toggles TCP stream multiplexing. This allows multiple requests // from a client to share a single TCP connection. If this value is true, // the server must have TCP multiplexing enabled as well. By default, this // value is true. - TcpMux bool `json:"tcp_mux"` + TCPMux bool `json:"tcp_mux"` // User specifies a prefix for proxy names to distinguish them from other // clients. If this value is not "", proxy names will automatically be // changed to "{user}.{proxy_name}". By default, this value is "". User string `json:"user"` - // DnsServer specifies a DNS server address for FRPC to use. If this value + // DNSServer specifies a DNS server address for FRPC to use. If this value // is "", the default DNS will be used. By default, this value is "". - DnsServer string `json:"dns_server"` + DNSServer string `json:"dns_server"` // LoginFailExit controls whether or not the client should exit after a // failed login attempt. If false, the client will retry until a login // attempt succeeds. By default, this value is true. @@ -104,8 +104,20 @@ type ClientCommonConf struct { // is "tcp". Protocol string `json:"protocol"` // TLSEnable specifies whether or not TLS should be used when communicating - // with the server. + // with the server. If "tls_cert_file" and "tls_key_file" are valid, + // client will load the supplied tls configuration. TLSEnable bool `json:"tls_enable"` + // ClientTLSCertPath specifies the path of the cert file that client will + // load. It only works when "tls_enable" is true and "tls_key_file" is valid. + TLSCertFile string `json:"tls_cert_file"` + // ClientTLSKeyPath specifies the path of the secret key file that client + // will load. It only works when "tls_enable" is true and "tls_cert_file" + // are valid. + TLSKeyFile string `json:"tls_key_file"` + // TrustedCaFile specifies the path of the trusted ca file that will load. + // It only works when "tls_enable" is valid and tls configuration of server + // has been specified. + TLSTrustedCaFile string `json:"tls_trusted_ca_file"` // HeartBeatInterval specifies at what interval heartbeats are sent to the // server, in seconds. It is not recommended to change this value. By // default, this value is 30. @@ -116,6 +128,9 @@ type ClientCommonConf struct { HeartBeatTimeout int64 `json:"heartbeat_timeout"` // Client meta info Metas map[string]string `json:"metas"` + // UDPPacketSize specifies the udp packet size + // By default, this value is 1500 + UDPPacketSize int64 `json:"udp_packet_size"` } // GetDefaultClientConf returns a client configuration with default values. @@ -123,7 +138,7 @@ func GetDefaultClientConf() ClientCommonConf { return ClientCommonConf{ ServerAddr: "0.0.0.0", ServerPort: 7000, - HttpProxy: os.Getenv("http_proxy"), + HTTPProxy: os.Getenv("http_proxy"), LogFile: "console", LogWay: "console", LogLevel: "info", @@ -135,16 +150,20 @@ func GetDefaultClientConf() ClientCommonConf { AdminPwd: "", AssetsDir: "", PoolCount: 1, - TcpMux: true, + TCPMux: true, User: "", - DnsServer: "", + DNSServer: "", LoginFailExit: true, Start: make(map[string]struct{}), Protocol: "tcp", TLSEnable: false, + TLSCertFile: "", + TLSKeyFile: "", + TLSTrustedCaFile: "", HeartBeatInterval: 30, HeartBeatTimeout: 90, Metas: make(map[string]string), + UDPPacketSize: 1500, } } @@ -156,7 +175,7 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error return ClientCommonConf{}, fmt.Errorf("parse ini conf file error: %v", err) } - cfg.AuthClientConfig = auth.UnmarshalAuthClientConfFromIni(conf) + cfg.ClientConfig = auth.UnmarshalClientConfFromIni(conf) var ( tmpStr string @@ -181,7 +200,7 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error } if tmpStr, ok = conf.Get("common", "http_proxy"); ok { - cfg.HttpProxy = tmpStr + cfg.HTTPProxy = tmpStr } if tmpStr, ok = conf.Get("common", "log_file"); ok { @@ -235,9 +254,9 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error } if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" { - cfg.TcpMux = false + cfg.TCPMux = false } else { - cfg.TcpMux = true + cfg.TCPMux = true } if tmpStr, ok = conf.Get("common", "user"); ok { @@ -245,7 +264,7 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error } if tmpStr, ok = conf.Get("common", "dns_server"); ok { - cfg.DnsServer = tmpStr + cfg.DNSServer = tmpStr } if tmpStr, ok = conf.Get("common", "start"); ok { @@ -276,28 +295,45 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error cfg.TLSEnable = false } + if tmpStr, ok = conf.Get("common", "tls_cert_file"); ok { + cfg.TLSCertFile = tmpStr + } + + if tmpStr, ok := conf.Get("common", "tls_key_file"); ok { + cfg.TLSKeyFile = tmpStr + } + + if tmpStr, ok := conf.Get("common", "tls_trusted_ca_file"); ok { + cfg.TLSTrustedCaFile = tmpStr + } + 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") return - } else { - cfg.HeartBeatTimeout = v } + cfg.HeartBeatTimeout = v } if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid heartbeat_interval") return - } else { - cfg.HeartBeatInterval = v } + cfg.HeartBeatInterval = v } for k, v := range conf.Section("common") { if strings.HasPrefix(k, "meta_") { cfg.Metas[strings.TrimPrefix(k, "meta_")] = v } } + if tmpStr, ok = conf.Get("common", "udp_packet_size"); ok { + if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { + err = fmt.Errorf("Parse conf error: invalid udp_packet_size") + return + } + cfg.UDPPacketSize = v + } return } @@ -311,5 +347,19 @@ func (cfg *ClientCommonConf) Check() (err error) { err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval") return } + + if cfg.TLSEnable == false { + if cfg.TLSCertFile != "" { + fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false") + } + + if cfg.TLSKeyFile != "" { + fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false") + } + + if cfg.TLSTrustedCaFile != "" { + fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false") + } + } return } diff --git a/models/config/proxy.go b/models/config/proxy.go index a1e34ce8..7c143a1a 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -33,14 +33,14 @@ var ( func init() { proxyConfTypeMap = make(map[string]reflect.Type) - proxyConfTypeMap[consts.TcpProxy] = reflect.TypeOf(TcpProxyConf{}) - proxyConfTypeMap[consts.TcpMuxProxy] = reflect.TypeOf(TcpMuxProxyConf{}) - proxyConfTypeMap[consts.UdpProxy] = reflect.TypeOf(UdpProxyConf{}) - proxyConfTypeMap[consts.HttpProxy] = reflect.TypeOf(HttpProxyConf{}) - proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{}) - proxyConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpProxyConf{}) - proxyConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpProxyConf{}) - proxyConfTypeMap[consts.SudpProxy] = reflect.TypeOf(SudpProxyConf{}) + proxyConfTypeMap[consts.TCPProxy] = reflect.TypeOf(TCPProxyConf{}) + proxyConfTypeMap[consts.TCPMuxProxy] = reflect.TypeOf(TCPMuxProxyConf{}) + proxyConfTypeMap[consts.UDPProxy] = reflect.TypeOf(UDPProxyConf{}) + proxyConfTypeMap[consts.HTTPProxy] = reflect.TypeOf(HTTPProxyConf{}) + proxyConfTypeMap[consts.HTTPSProxy] = reflect.TypeOf(HTTPSProxyConf{}) + proxyConfTypeMap[consts.STCPProxy] = reflect.TypeOf(STCPProxyConf{}) + proxyConfTypeMap[consts.XTCPProxy] = reflect.TypeOf(XTCPProxyConf{}) + proxyConfTypeMap[consts.SUDPProxy] = reflect.TypeOf(SUDPProxyConf{}) } // NewConfByType creates a empty ProxyConf object by proxyType. @@ -66,7 +66,7 @@ type ProxyConf interface { func NewProxyConfFromMsg(pMsg *msg.NewProxy, serverCfg ServerCommonConf) (cfg ProxyConf, err error) { if pMsg.ProxyType == "" { - pMsg.ProxyType = consts.TcpProxy + pMsg.ProxyType = consts.TCPProxy } cfg = NewConfByType(pMsg.ProxyType) @@ -82,8 +82,8 @@ func NewProxyConfFromMsg(pMsg *msg.NewProxy, serverCfg ServerCommonConf) (cfg Pr func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) { proxyType := section["type"] if proxyType == "" { - proxyType = consts.TcpProxy - section["type"] = consts.TcpProxy + proxyType = consts.TCPProxy + section["type"] = consts.TCPProxy } cfg = NewConfByType(proxyType) if cfg == nil { @@ -210,14 +210,14 @@ func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section i } if cfg.HealthCheckType == "tcp" && cfg.Plugin == "" { - cfg.HealthCheckAddr = cfg.LocalIp + fmt.Sprintf(":%d", cfg.LocalPort) + cfg.HealthCheckAddr = cfg.LocalIP + fmt.Sprintf(":%d", cfg.LocalPort) } - if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckUrl != "" { - s := fmt.Sprintf("http://%s:%d", cfg.LocalIp, cfg.LocalPort) - if !strings.HasPrefix(cfg.HealthCheckUrl, "/") { + if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckURL != "" { + s := fmt.Sprintf("http://%s:%d", cfg.LocalIP, cfg.LocalPort) + if !strings.HasPrefix(cfg.HealthCheckURL, "/") { s += "/" } - cfg.HealthCheckUrl = s + cfg.HealthCheckUrl + cfg.HealthCheckURL = s + cfg.HealthCheckURL } cfg.Metas = make(map[string]string) @@ -280,9 +280,8 @@ func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section in if tmpStr, ok = section["remote_port"]; ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { return fmt.Errorf("Parse conf error: proxy [%s] remote_port error", name) - } else { - cfg.RemotePort = int(v) } + cfg.RemotePort = int(v) } else { return fmt.Errorf("Parse conf error: proxy [%s] remote_port not found", name) } @@ -377,8 +376,8 @@ func (cfg *DomainConf) checkForSvr(serverCfg ServerCommonConf) (err error) { // LocalSvrConf configures what location the client will proxy to, or what // plugin will be used. type LocalSvrConf struct { - // LocalIp specifies the IP address or host name to proxy to. - LocalIp string `json:"local_ip"` + // LocalIP specifies the IP address or host name to proxy to. + LocalIP string `json:"local_ip"` // LocalPort specifies the port to proxy to. LocalPort int `json:"local_port"` @@ -392,7 +391,7 @@ type LocalSvrConf struct { } func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool { - if cfg.LocalIp != cmp.LocalIp || + if cfg.LocalIP != cmp.LocalIP || cfg.LocalPort != cmp.LocalPort { return false } @@ -420,8 +419,8 @@ func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section in } } } else { - if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" { - cfg.LocalIp = "127.0.0.1" + if cfg.LocalIP = section["local_ip"]; cfg.LocalIP == "" { + cfg.LocalIP = "127.0.0.1" } if tmpStr, ok := section["local_port"]; ok { @@ -437,7 +436,7 @@ func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section in func (cfg *LocalSvrConf) checkForCli() (err error) { if cfg.Plugin == "" { - if cfg.LocalIp == "" { + if cfg.LocalIP == "" { err = fmt.Errorf("local ip or plugin is required") return } @@ -460,7 +459,7 @@ type HealthCheckConf struct { // server. If a connection cannot be established, the health check fails. // // If the type is "http", a GET request will be made to the endpoint - // specified by HealthCheckUrl. If the response is not a 200, the health + // specified by HealthCheckURL. If the response is not a 200, the health // check fails. HealthCheckType string `json:"health_check_type"` // tcp | http // HealthCheckTimeoutS specifies the number of seconds to wait for a health @@ -473,9 +472,9 @@ type HealthCheckConf struct { // HealthCheckIntervalS specifies the time in seconds between health // checks. By default, this value is 10. HealthCheckIntervalS int `json:"health_check_interval_s"` - // HealthCheckUrl specifies the address to send health checks to if the + // HealthCheckURL specifies the address to send health checks to if the // health check type is "http". - HealthCheckUrl string `json:"health_check_url"` + HealthCheckURL string `json:"health_check_url"` // HealthCheckAddr specifies the address to connect to if the health check // type is "tcp". HealthCheckAddr string `json:"-"` @@ -486,7 +485,7 @@ func (cfg *HealthCheckConf) compare(cmp *HealthCheckConf) bool { cfg.HealthCheckTimeoutS != cmp.HealthCheckTimeoutS || cfg.HealthCheckMaxFailed != cmp.HealthCheckMaxFailed || cfg.HealthCheckIntervalS != cmp.HealthCheckIntervalS || - cfg.HealthCheckUrl != cmp.HealthCheckUrl { + cfg.HealthCheckURL != cmp.HealthCheckURL { return false } return true @@ -494,7 +493,7 @@ func (cfg *HealthCheckConf) compare(cmp *HealthCheckConf) bool { func (cfg *HealthCheckConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { cfg.HealthCheckType = section["health_check_type"] - cfg.HealthCheckUrl = section["health_check_url"] + cfg.HealthCheckURL = section["health_check_url"] if tmpStr, ok := section["health_check_timeout_s"]; ok { if cfg.HealthCheckTimeoutS, err = strconv.Atoi(tmpStr); err != nil { @@ -521,7 +520,7 @@ func (cfg *HealthCheckConf) checkForCli() error { return fmt.Errorf("unsupport health check type") } if cfg.HealthCheckType != "" { - if cfg.HealthCheckType == "http" && cfg.HealthCheckUrl == "" { + if cfg.HealthCheckType == "http" && cfg.HealthCheckURL == "" { return fmt.Errorf("health_check_url is required for health check type 'http'") } } @@ -529,13 +528,13 @@ func (cfg *HealthCheckConf) checkForCli() error { } // TCP -type TcpProxyConf struct { +type TCPProxyConf struct { BaseProxyConf BindInfoConf } -func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*TcpProxyConf) +func (cfg *TCPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*TCPProxyConf) if !ok { return false } @@ -547,12 +546,12 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *TCPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.BindInfoConf.UnmarshalFromMsg(pMsg) } -func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *TCPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -562,30 +561,30 @@ func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section in return } -func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *TCPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *TcpProxyConf) CheckForCli() (err error) { +func (cfg *TCPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return err } return } -func (cfg *TcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil } +func (cfg *TCPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil } // TCP Multiplexer -type TcpMuxProxyConf struct { +type TCPMuxProxyConf struct { BaseProxyConf DomainConf Multiplexer string `json:"multiplexer"` } -func (cfg *TcpMuxProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*TcpMuxProxyConf) +func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*TCPMuxProxyConf) if !ok { return false } @@ -598,13 +597,13 @@ func (cfg *TcpMuxProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *TcpMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *TCPMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.DomainConf.UnmarshalFromMsg(pMsg) cfg.Multiplexer = pMsg.Multiplexer } -func (cfg *TcpMuxProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *TCPMuxProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -613,37 +612,37 @@ func (cfg *TcpMuxProxyConf) UnmarshalFromIni(prefix string, name string, section } cfg.Multiplexer = section["multiplexer"] - if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer { + if cfg.Multiplexer != consts.HTTPConnectTCPMultiplexer { return fmt.Errorf("parse conf error: proxy [%s] incorrect multiplexer [%s]", name, cfg.Multiplexer) } return } -func (cfg *TcpMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) cfg.DomainConf.MarshalToMsg(pMsg) pMsg.Multiplexer = cfg.Multiplexer } -func (cfg *TcpMuxProxyConf) CheckForCli() (err error) { +func (cfg *TCPMuxProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return err } if err = cfg.DomainConf.checkForCli(); err != nil { return err } - if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer { + if cfg.Multiplexer != consts.HTTPConnectTCPMultiplexer { return fmt.Errorf("parse conf error: incorrect multiplexer [%s]", cfg.Multiplexer) } return } -func (cfg *TcpMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { - if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer { +func (cfg *TCPMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { + if cfg.Multiplexer != consts.HTTPConnectTCPMultiplexer { return fmt.Errorf("proxy [%s] incorrect multiplexer [%s]", cfg.ProxyName, cfg.Multiplexer) } - if cfg.Multiplexer == consts.HttpConnectTcpMultiplexer && serverCfg.TcpMuxHttpConnectPort == 0 { + if cfg.Multiplexer == consts.HTTPConnectTCPMultiplexer && serverCfg.TCPMuxHTTPConnectPort == 0 { return fmt.Errorf("proxy [%s] type [tcpmux] with multiplexer [httpconnect] requires tcpmux_httpconnect_port configuration", cfg.ProxyName) } @@ -655,13 +654,13 @@ func (cfg *TcpMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) } // UDP -type UdpProxyConf struct { +type UDPProxyConf struct { BaseProxyConf BindInfoConf } -func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*UdpProxyConf) +func (cfg *UDPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*UDPProxyConf) if !ok { return false } @@ -673,12 +672,12 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *UDPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.BindInfoConf.UnmarshalFromMsg(pMsg) } -func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *UDPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -688,34 +687,34 @@ func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section in return } -func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *UDPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) cfg.BindInfoConf.MarshalToMsg(pMsg) } -func (cfg *UdpProxyConf) CheckForCli() (err error) { +func (cfg *UDPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } return } -func (cfg *UdpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil } +func (cfg *UDPProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil } // HTTP -type HttpProxyConf struct { +type HTTPProxyConf struct { BaseProxyConf DomainConf Locations []string `json:"locations"` - HttpUser string `json:"http_user"` - HttpPwd string `json:"http_pwd"` + HTTPUser string `json:"http_user"` + HTTPPwd string `json:"http_pwd"` HostHeaderRewrite string `json:"host_header_rewrite"` Headers map[string]string `json:"headers"` } -func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*HttpProxyConf) +func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*HTTPProxyConf) if !ok { return false } @@ -724,36 +723,36 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool { !cfg.DomainConf.compare(&cmpConf.DomainConf) || strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") || cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite || - cfg.HttpUser != cmpConf.HttpUser || - cfg.HttpPwd != cmpConf.HttpPwd || + cfg.HTTPUser != cmpConf.HTTPUser || + cfg.HTTPPwd != cmpConf.HTTPPwd || len(cfg.Headers) != len(cmpConf.Headers) { return false } for k, v := range cfg.Headers { - if v2, ok := cmpConf.Headers[k]; !ok { + v2, ok := cmpConf.Headers[k] + if !ok { + return false + } + if v != v2 { return false - } else { - if v != v2 { - return false - } } } return true } -func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *HTTPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.DomainConf.UnmarshalFromMsg(pMsg) cfg.Locations = pMsg.Locations cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite - cfg.HttpUser = pMsg.HttpUser - cfg.HttpPwd = pMsg.HttpPwd + cfg.HTTPUser = pMsg.HTTPUser + cfg.HTTPPwd = pMsg.HTTPPwd cfg.Headers = pMsg.Headers } -func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *HTTPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -772,8 +771,8 @@ func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section i } cfg.HostHeaderRewrite = section["host_header_rewrite"] - cfg.HttpUser = section["http_user"] - cfg.HttpPwd = section["http_pwd"] + cfg.HTTPUser = section["http_user"] + cfg.HTTPPwd = section["http_pwd"] cfg.Headers = make(map[string]string) for k, v := range section { @@ -784,18 +783,18 @@ func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } -func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) cfg.DomainConf.MarshalToMsg(pMsg) pMsg.Locations = cfg.Locations pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite - pMsg.HttpUser = cfg.HttpUser - pMsg.HttpPwd = cfg.HttpPwd + pMsg.HTTPUser = cfg.HTTPUser + pMsg.HTTPPwd = cfg.HTTPPwd pMsg.Headers = cfg.Headers } -func (cfg *HttpProxyConf) CheckForCli() (err error) { +func (cfg *HTTPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } @@ -805,8 +804,8 @@ func (cfg *HttpProxyConf) CheckForCli() (err error) { return } -func (cfg *HttpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { - if serverCfg.VhostHttpPort == 0 { +func (cfg *HTTPProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { + if serverCfg.VhostHTTPPort == 0 { return fmt.Errorf("type [http] not support when vhost_http_port is not set") } if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil { @@ -817,13 +816,13 @@ func (cfg *HttpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { } // HTTPS -type HttpsProxyConf struct { +type HTTPSProxyConf struct { BaseProxyConf DomainConf } -func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*HttpsProxyConf) +func (cfg *HTTPSProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*HTTPSProxyConf) if !ok { return false } @@ -835,12 +834,12 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *HTTPSProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.DomainConf.UnmarshalFromMsg(pMsg) } -func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *HTTPSProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -850,12 +849,12 @@ func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section return } -func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *HTTPSProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) cfg.DomainConf.MarshalToMsg(pMsg) } -func (cfg *HttpsProxyConf) CheckForCli() (err error) { +func (cfg *HTTPSProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } @@ -865,8 +864,8 @@ func (cfg *HttpsProxyConf) CheckForCli() (err error) { return } -func (cfg *HttpsProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { - if serverCfg.VhostHttpsPort == 0 { +func (cfg *HTTPSProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { + if serverCfg.VhostHTTPSPort == 0 { return fmt.Errorf("type [https] not support when vhost_https_port is not set") } if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil { @@ -877,15 +876,15 @@ func (cfg *HttpsProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { } // SUDP -type SudpProxyConf struct { +type SUDPProxyConf struct { BaseProxyConf Role string `json:"role"` Sk string `json:"sk"` } -func (cfg *SudpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*SudpProxyConf) +func (cfg *SUDPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*SUDPProxyConf) if !ok { return false } @@ -898,7 +897,7 @@ func (cfg *SudpProxyConf) Compare(cmp ProxyConf) bool { return true } -func (cfg *SudpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *SUDPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -916,12 +915,12 @@ func (cfg *SudpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } -func (cfg *SudpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *SUDPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) pMsg.Sk = cfg.Sk } -func (cfg *SudpProxyConf) CheckForCli() (err error) { +func (cfg *SUDPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } @@ -932,26 +931,26 @@ func (cfg *SudpProxyConf) CheckForCli() (err error) { return } -func (cfg *SudpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { +func (cfg *SUDPProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { return } // Only for role server. -func (cfg *SudpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *SUDPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.Sk = pMsg.Sk } // STCP -type StcpProxyConf struct { +type STCPProxyConf struct { BaseProxyConf Role string `json:"role"` Sk string `json:"sk"` } -func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*StcpProxyConf) +func (cfg *STCPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*STCPProxyConf) if !ok { return false } @@ -965,12 +964,12 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool { } // Only for role server. -func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *STCPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.Sk = pMsg.Sk } -func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *STCPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -988,12 +987,12 @@ func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } -func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *STCPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) pMsg.Sk = cfg.Sk } -func (cfg *StcpProxyConf) CheckForCli() (err error) { +func (cfg *STCPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } @@ -1004,20 +1003,20 @@ func (cfg *StcpProxyConf) CheckForCli() (err error) { return } -func (cfg *StcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { +func (cfg *STCPProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { return } // XTCP -type XtcpProxyConf struct { +type XTCPProxyConf struct { BaseProxyConf Role string `json:"role"` Sk string `json:"sk"` } -func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { - cmpConf, ok := cmp.(*XtcpProxyConf) +func (cfg *XTCPProxyConf) Compare(cmp ProxyConf) bool { + cmpConf, ok := cmp.(*XTCPProxyConf) if !ok { return false } @@ -1032,12 +1031,12 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool { } // Only for role server. -func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { +func (cfg *XTCPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.UnmarshalFromMsg(pMsg) cfg.Sk = pMsg.Sk } -func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *XTCPProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil { return } @@ -1055,12 +1054,12 @@ func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section i return } -func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { +func (cfg *XTCPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { cfg.BaseProxyConf.MarshalToMsg(pMsg) pMsg.Sk = cfg.Sk } -func (cfg *XtcpProxyConf) CheckForCli() (err error) { +func (cfg *XTCPProxyConf) CheckForCli() (err error) { if err = cfg.BaseProxyConf.checkForCli(); err != nil { return } @@ -1071,7 +1070,7 @@ func (cfg *XtcpProxyConf) CheckForCli() (err error) { return } -func (cfg *XtcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { +func (cfg *XTCPProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { return } diff --git a/models/config/server_common.go b/models/config/server_common.go index a6aa969e..1690aa0e 100644 --- a/models/config/server_common.go +++ b/models/config/server_common.go @@ -30,40 +30,40 @@ import ( // recommended to use GetDefaultServerConf instead of creating this object // directly, so that all unspecified fields have reasonable default values. type ServerCommonConf struct { - auth.AuthServerConfig + auth.ServerConfig // BindAddr specifies the address that the server binds to. By default, // this value is "0.0.0.0". BindAddr string `json:"bind_addr"` // BindPort specifies the port that the server listens on. By default, this // value is 7000. BindPort int `json:"bind_port"` - // BindUdpPort specifies the UDP port that the server listens on. If this + // BindUDPPort specifies the UDP port that the server listens on. If this // value is 0, the server will not listen for UDP connections. By default, // this value is 0 - BindUdpPort int `json:"bind_udp_port"` - // BindKcpPort specifies the KCP port that the server listens on. If this + BindUDPPort int `json:"bind_udp_port"` + // KCPBindPort specifies the KCP port that the server listens on. If this // value is 0, the server will not listen for KCP connections. By default, // this value is 0. - KcpBindPort int `json:"kcp_bind_port"` + KCPBindPort int `json:"kcp_bind_port"` // ProxyBindAddr specifies the address that the proxy binds to. This value // may be the same as BindAddr. By default, this value is "0.0.0.0". ProxyBindAddr string `json:"proxy_bind_addr"` - // VhostHttpPort specifies the port that the server listens for HTTP Vhost + // VhostHTTPPort specifies the port that the server listens for HTTP Vhost // requests. If this value is 0, the server will not listen for HTTP // requests. By default, this value is 0. - VhostHttpPort int `json:"vhost_http_port"` - // VhostHttpsPort specifies the port that the server listens for HTTPS + VhostHTTPPort int `json:"vhost_http_port"` + // VhostHTTPSPort specifies the port that the server listens for HTTPS // Vhost requests. If this value is 0, the server will not listen for HTTPS // requests. By default, this value is 0. - VhostHttpsPort int `json:"vhost_https_port"` - // TcpMuxHttpConnectPort specifies the port that the server listens for TCP + VhostHTTPSPort int `json:"vhost_https_port"` + // TCPMuxHTTPConnectPort specifies the port that the server listens for TCP // HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP // requests on one single port. If it's not - it will listen on this value for // HTTP CONNECT requests. By default, this value is 0. - TcpMuxHttpConnectPort int `json:"tcpmux_httpconnect_port"` - // VhostHttpTimeout specifies the response header timeout for the Vhost + TCPMuxHTTPConnectPort int `json:"tcpmux_httpconnect_port"` + // VhostHTTPTimeout specifies the response header timeout for the Vhost // HTTP server, in seconds. By default, this value is 60. - VhostHttpTimeout int64 `json:"vhost_http_timeout"` + VhostHTTPTimeout int64 `json:"vhost_http_timeout"` // DashboardAddr specifies the address that the dashboard binds to. By // default, this value is "0.0.0.0". DashboardAddr string `json:"dashboard_addr"` @@ -113,10 +113,10 @@ type ServerCommonConf struct { // "test", the resulting URL would be "test.frps.com". By default, this // value is "". SubDomainHost string `json:"subdomain_host"` - // TcpMux toggles TCP stream multiplexing. This allows multiple requests + // TCPMux toggles TCP stream multiplexing. This allows multiple requests // from a client to share a single TCP connection. By default, this value // is true. - TcpMux bool `json:"tcp_mux"` + TCPMux bool `json:"tcp_mux"` // Custom404Page specifies a path to a custom 404 page to display. If this // value is "", a default page will be displayed. By default, this value is // "". @@ -133,9 +133,24 @@ type ServerCommonConf struct { // may proxy to. If this value is 0, no limit will be applied. By default, // this value is 0. MaxPortsPerClient int64 `json:"max_ports_per_client"` - // TlsOnly specifies whether to only accept TLS-encrypted connections. By - // default, the value is false. - TlsOnly bool `json:"tls_only"` + // TLSOnly specifies whether to only accept TLS-encrypted connections. + // By default, the value is false. + TLSOnly bool `json:"tls_only"` + // TLSCertFile specifies the path of the cert file that the server will + // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this + // supplied tls configuration. Otherwise, the server will use the tls + // configuration generated by itself. + TLSCertFile string `json:"tls_cert_file"` + // TLSKeyFile specifies the path of the secret key that the server will + // load. If "tls_cert_file", "tls_key_file" are valid, the server will use this + // supplied tls configuration. Otherwise, the server will use the tls + // configuration generated by itself. + TLSKeyFile string `json:"tls_key_file"` + // TLSTrustedCaFile specifies the paths of the client cert files that the + // server will load. It only works when "tls_only" is true. If + // "tls_trusted_ca_file" is valid, the server will verify each client's + // certificate. + TLSTrustedCaFile string `json:"tls_trusted_ca_file"` // HeartBeatTimeout specifies the maximum time to wait for a heartbeat // before terminating the connection. It is not recommended to change this // value. By default, this value is 90. @@ -145,6 +160,9 @@ type ServerCommonConf struct { UserConnTimeout int64 `json:"user_conn_timeout"` // HTTPPlugins specify the server plugins support HTTP protocol. HTTPPlugins map[string]plugin.HTTPPluginOptions `json:"http_plugins"` + // UDPPacketSize specifies the UDP packet size + // By default, this value is 1500 + UDPPacketSize int64 `json:"udp_packet_size"` } // GetDefaultServerConf returns a server configuration with reasonable @@ -153,13 +171,13 @@ func GetDefaultServerConf() ServerCommonConf { return ServerCommonConf{ BindAddr: "0.0.0.0", BindPort: 7000, - BindUdpPort: 0, - KcpBindPort: 0, + BindUDPPort: 0, + KCPBindPort: 0, ProxyBindAddr: "0.0.0.0", - VhostHttpPort: 0, - VhostHttpsPort: 0, - TcpMuxHttpConnectPort: 0, - VhostHttpTimeout: 60, + VhostHTTPPort: 0, + VhostHTTPSPort: 0, + TCPMuxHTTPConnectPort: 0, + VhostHTTPTimeout: 60, DashboardAddr: "0.0.0.0", DashboardPort: 0, DashboardUser: "admin", @@ -173,15 +191,19 @@ func GetDefaultServerConf() ServerCommonConf { DisableLogColor: false, DetailedErrorsToClient: true, SubDomainHost: "", - TcpMux: true, + TCPMux: true, AllowPorts: make(map[int]struct{}), MaxPoolCount: 5, MaxPortsPerClient: 0, - TlsOnly: false, + TLSOnly: false, + TLSCertFile: "", + TLSKeyFile: "", + TLSTrustedCaFile: "", HeartBeatTimeout: 90, UserConnTimeout: 10, Custom404Page: "", HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), + UDPPacketSize: 1500, } } @@ -198,7 +220,7 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error UnmarshalPluginsFromIni(conf, &cfg) - cfg.AuthServerConfig = auth.UnmarshalAuthServerConfFromIni(conf) + cfg.ServerConfig = auth.UnmarshalServerConfFromIni(conf) var ( tmpStr string @@ -213,27 +235,24 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid bind_port") return - } else { - cfg.BindPort = int(v) } + cfg.BindPort = int(v) } if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid bind_udp_port") return - } else { - cfg.BindUdpPort = int(v) } + cfg.BindUDPPort = int(v) } if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid kcp_bind_port") return - } else { - cfg.KcpBindPort = int(v) } + cfg.KCPBindPort = int(v) } if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok { @@ -246,33 +265,30 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid vhost_http_port") return - } else { - cfg.VhostHttpPort = int(v) } + cfg.VhostHTTPPort = int(v) } else { - cfg.VhostHttpPort = 0 + cfg.VhostHTTPPort = 0 } if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid vhost_https_port") return - } else { - cfg.VhostHttpsPort = int(v) } + cfg.VhostHTTPSPort = int(v) } else { - cfg.VhostHttpsPort = 0 + cfg.VhostHTTPSPort = 0 } if tmpStr, ok = conf.Get("common", "tcpmux_httpconnect_port"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid tcpmux_httpconnect_port") return - } else { - cfg.TcpMuxHttpConnectPort = int(v) } + cfg.TCPMuxHTTPConnectPort = int(v) } else { - cfg.TcpMuxHttpConnectPort = 0 + cfg.TCPMuxHTTPConnectPort = 0 } if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok { @@ -280,9 +296,8 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if errRet != nil || v < 0 { err = fmt.Errorf("Parse conf error: invalid vhost_http_timeout") return - } else { - cfg.VhostHttpTimeout = v } + cfg.VhostHTTPTimeout = v } if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok { @@ -295,9 +310,8 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid dashboard_port") return - } else { - cfg.DashboardPort = int(v) } + cfg.DashboardPort = int(v) } else { cfg.DashboardPort = 0 } @@ -365,26 +379,26 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid max_pool_count") return - } else { - if v < 0 { - err = fmt.Errorf("Parse conf error: invalid max_pool_count") - return - } - cfg.MaxPoolCount = v } + + if v < 0 { + err = fmt.Errorf("Parse conf error: invalid max_pool_count") + return + } + cfg.MaxPoolCount = v } if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok { if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { err = fmt.Errorf("Parse conf error: invalid max_ports_per_client") return - } else { - if v < 0 { - err = fmt.Errorf("Parse conf error: invalid max_ports_per_client") - return - } - cfg.MaxPortsPerClient = v } + + if v < 0 { + err = fmt.Errorf("Parse conf error: invalid max_ports_per_client") + return + } + cfg.MaxPortsPerClient = v } if tmpStr, ok = conf.Get("common", "subdomain_host"); ok { @@ -392,9 +406,9 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error } if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" { - cfg.TcpMux = false + cfg.TCPMux = false } else { - cfg.TcpMux = true + cfg.TCPMux = true } if tmpStr, ok = conf.Get("common", "custom_404_page"); ok { @@ -406,16 +420,37 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error if errRet != nil { err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect") return - } else { - cfg.HeartBeatTimeout = v } + cfg.HeartBeatTimeout = v } if tmpStr, ok = conf.Get("common", "tls_only"); ok && tmpStr == "true" { - cfg.TlsOnly = true + cfg.TLSOnly = true } else { - cfg.TlsOnly = false + cfg.TLSOnly = false } + + if tmpStr, ok = conf.Get("common", "udp_packet_size"); ok { + if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil { + err = fmt.Errorf("Parse conf error: invalid udp_packet_size") + return + } + cfg.UDPPacketSize = v + } + + if tmpStr, ok := conf.Get("common", "tls_cert_file"); ok { + cfg.TLSCertFile = tmpStr + } + + if tmpStr, ok := conf.Get("common", "tls_key_file"); ok { + cfg.TLSKeyFile = tmpStr + } + + if tmpStr, ok := conf.Get("common", "tls_trusted_ca_file"); ok { + cfg.TLSTrustedCaFile = tmpStr + cfg.TLSOnly = true + } + return } @@ -429,7 +464,7 @@ func UnmarshalPluginsFromIni(sections ini.File, cfg *ServerCommonConf) { Path: section["path"], Ops: strings.Split(section["ops"], ","), } - for i, _ := range options.Ops { + for i := range options.Ops { options.Ops[i] = strings.TrimSpace(options.Ops[i]) } cfg.HTTPPlugins[name] = options @@ -437,6 +472,6 @@ func UnmarshalPluginsFromIni(sections ini.File, cfg *ServerCommonConf) { } } -func (cfg *ServerCommonConf) Check() (err error) { - return +func (cfg *ServerCommonConf) Check() error { + return nil } diff --git a/models/config/visitor.go b/models/config/visitor.go index ad9ff841..a3568410 100644 --- a/models/config/visitor.go +++ b/models/config/visitor.go @@ -30,9 +30,9 @@ var ( func init() { visitorConfTypeMap = make(map[string]reflect.Type) - visitorConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpVisitorConf{}) - visitorConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpVisitorConf{}) - visitorConfTypeMap[consts.SudpProxy] = reflect.TypeOf(SudpVisitorConf{}) + visitorConfTypeMap[consts.STCPProxy] = reflect.TypeOf(STCPVisitorConf{}) + visitorConfTypeMap[consts.XTCPProxy] = reflect.TypeOf(XTCPVisitorConf{}) + visitorConfTypeMap[consts.SUDPProxy] = reflect.TypeOf(SUDPVisitorConf{}) } type VisitorConf interface { @@ -153,12 +153,12 @@ func (cfg *BaseVisitorConf) UnmarshalFromIni(prefix string, name string, section return nil } -type SudpVisitorConf struct { +type SUDPVisitorConf struct { BaseVisitorConf } -func (cfg *SudpVisitorConf) Compare(cmp VisitorConf) bool { - cmpConf, ok := cmp.(*SudpVisitorConf) +func (cfg *SUDPVisitorConf) Compare(cmp VisitorConf) bool { + cmpConf, ok := cmp.(*SUDPVisitorConf) if !ok { return false } @@ -169,26 +169,26 @@ func (cfg *SudpVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *SudpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil { return } return } -func (cfg *SudpVisitorConf) Check() (err error) { +func (cfg *SUDPVisitorConf) Check() (err error) { if err = cfg.BaseVisitorConf.check(); err != nil { return } return } -type StcpVisitorConf struct { +type STCPVisitorConf struct { BaseVisitorConf } -func (cfg *StcpVisitorConf) Compare(cmp VisitorConf) bool { - cmpConf, ok := cmp.(*StcpVisitorConf) +func (cfg *STCPVisitorConf) Compare(cmp VisitorConf) bool { + cmpConf, ok := cmp.(*STCPVisitorConf) if !ok { return false } @@ -199,26 +199,26 @@ func (cfg *StcpVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *StcpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil { return } return } -func (cfg *StcpVisitorConf) Check() (err error) { +func (cfg *STCPVisitorConf) Check() (err error) { if err = cfg.BaseVisitorConf.check(); err != nil { return } return } -type XtcpVisitorConf struct { +type XTCPVisitorConf struct { BaseVisitorConf } -func (cfg *XtcpVisitorConf) Compare(cmp VisitorConf) bool { - cmpConf, ok := cmp.(*XtcpVisitorConf) +func (cfg *XTCPVisitorConf) Compare(cmp VisitorConf) bool { + cmpConf, ok := cmp.(*XTCPVisitorConf) if !ok { return false } @@ -229,14 +229,14 @@ func (cfg *XtcpVisitorConf) Compare(cmp VisitorConf) bool { return true } -func (cfg *XtcpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { +func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) { if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil { return } return } -func (cfg *XtcpVisitorConf) Check() (err error) { +func (cfg *XTCPVisitorConf) Check() (err error) { if err = cfg.BaseVisitorConf.check(); err != nil { return } diff --git a/models/consts/consts.go b/models/consts/consts.go index e63da54a..907bc73e 100644 --- a/models/consts/consts.go +++ b/models/consts/consts.go @@ -23,19 +23,19 @@ var ( Offline string = "offline" // proxy type - TcpProxy string = "tcp" - UdpProxy string = "udp" - TcpMuxProxy string = "tcpmux" - HttpProxy string = "http" - HttpsProxy string = "https" - StcpProxy string = "stcp" - XtcpProxy string = "xtcp" - SudpProxy string = "sudp" + TCPProxy string = "tcp" + UDPProxy string = "udp" + TCPMuxProxy string = "tcpmux" + HTTPProxy string = "http" + HTTPSProxy string = "https" + STCPProxy string = "stcp" + XTCPProxy string = "xtcp" + SUDPProxy string = "sudp" // authentication method TokenAuthMethod string = "token" OidcAuthMethod string = "oidc" - // tcp multiplexer - HttpConnectTcpMultiplexer string = "httpconnect" + // TCP multiplexer + HTTPConnectTCPMultiplexer string = "httpconnect" ) diff --git a/models/metrics/mem/server.go b/models/metrics/mem/server.go index d7c191fa..307583ad 100644 --- a/models/metrics/mem/server.go +++ b/models/metrics/mem/server.go @@ -177,12 +177,12 @@ func (m *serverMetrics) GetServer() *ServerStats { s := &ServerStats{ TotalTrafficIn: m.info.TotalTrafficIn.TodayCount(), TotalTrafficOut: m.info.TotalTrafficOut.TodayCount(), - CurConns: m.info.CurConns.Count(), - ClientCounts: m.info.ClientCounts.Count(), + CurConns: int64(m.info.CurConns.Count()), + ClientCounts: int64(m.info.ClientCounts.Count()), ProxyTypeCounts: make(map[string]int64), } for k, v := range m.info.ProxyTypeCounts { - s.ProxyTypeCounts[k] = v.Count() + s.ProxyTypeCounts[k] = int64(v.Count()) } return s } @@ -202,7 +202,7 @@ func (m *serverMetrics) GetProxiesByType(proxyType string) []*ProxyStats { Type: proxyStats.ProxyType, TodayTrafficIn: proxyStats.TrafficIn.TodayCount(), TodayTrafficOut: proxyStats.TrafficOut.TodayCount(), - CurConns: proxyStats.CurConns.Count(), + CurConns: int64(proxyStats.CurConns.Count()), } if !proxyStats.LastStartTime.IsZero() { ps.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05") @@ -233,7 +233,7 @@ func (m *serverMetrics) GetProxiesByTypeAndName(proxyType string, proxyName stri Type: proxyStats.ProxyType, TodayTrafficIn: proxyStats.TrafficIn.TodayCount(), TodayTrafficOut: proxyStats.TrafficOut.TodayCount(), - CurConns: proxyStats.CurConns.Count(), + CurConns: int64(proxyStats.CurConns.Count()), } if !proxyStats.LastStartTime.IsZero() { res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05") diff --git a/models/msg/msg.go b/models/msg/msg.go index b08f1542..6e59b7cf 100644 --- a/models/msg/msg.go +++ b/models/msg/msg.go @@ -29,7 +29,7 @@ const ( TypeNewVisitorConnResp = '3' TypePing = 'h' TypePong = '4' - TypeUdpPacket = 'u' + TypeUDPPacket = 'u' TypeNatHoleVisitor = 'i' TypeNatHoleClient = 'n' TypeNatHoleResp = 'm' @@ -51,7 +51,7 @@ var ( TypeNewVisitorConnResp: NewVisitorConnResp{}, TypePing: Ping{}, TypePong: Pong{}, - TypeUdpPacket: UdpPacket{}, + TypeUDPPacket: UDPPacket{}, TypeNatHoleVisitor: NatHoleVisitor{}, TypeNatHoleClient: NatHoleClient{}, TypeNatHoleResp: NatHoleResp{}, @@ -69,7 +69,7 @@ type Login struct { User string `json:"user"` PrivilegeKey string `json:"privilege_key"` Timestamp int64 `json:"timestamp"` - RunId string `json:"run_id"` + RunID string `json:"run_id"` Metas map[string]string `json:"metas"` // Some global configures. @@ -78,8 +78,8 @@ type Login struct { type LoginResp struct { Version string `json:"version"` - RunId string `json:"run_id"` - ServerUdpPort int `json:"server_udp_port"` + RunID string `json:"run_id"` + ServerUDPPort int `json:"server_udp_port"` Error string `json:"error"` } @@ -100,8 +100,8 @@ type NewProxy struct { CustomDomains []string `json:"custom_domains"` SubDomain string `json:"subdomain"` Locations []string `json:"locations"` - HttpUser string `json:"http_user"` - HttpPwd string `json:"http_pwd"` + HTTPUser string `json:"http_user"` + HTTPPwd string `json:"http_pwd"` HostHeaderRewrite string `json:"host_header_rewrite"` Headers map[string]string `json:"headers"` @@ -123,7 +123,7 @@ type CloseProxy struct { } type NewWorkConn struct { - RunId string `json:"run_id"` + RunID string `json:"run_id"` PrivilegeKey string `json:"privilege_key"` Timestamp int64 `json:"timestamp"` } @@ -162,7 +162,7 @@ type Pong struct { Error string `json:"error"` } -type UdpPacket struct { +type UDPPacket struct { Content string `json:"c"` LocalAddr *net.UDPAddr `json:"l"` RemoteAddr *net.UDPAddr `json:"r"` diff --git a/models/nathole/nathole.go b/models/nathole/nathole.go index 0c33dfe4..e44e149b 100644 --- a/models/nathole/nathole.go +++ b/models/nathole/nathole.go @@ -23,16 +23,16 @@ type SidRequest struct { NotifyCh chan struct{} } -type NatHoleController struct { +type Controller struct { listener *net.UDPConn - clientCfgs map[string]*NatHoleClientCfg - sessions map[string]*NatHoleSession + clientCfgs map[string]*ClientCfg + sessions map[string]*Session mu sync.RWMutex } -func NewNatHoleController(udpBindAddr string) (nc *NatHoleController, err error) { +func NewController(udpBindAddr string) (nc *Controller, err error) { addr, err := net.ResolveUDPAddr("udp", udpBindAddr) if err != nil { return nil, err @@ -41,16 +41,16 @@ func NewNatHoleController(udpBindAddr string) (nc *NatHoleController, err error) if err != nil { return nil, err } - nc = &NatHoleController{ + nc = &Controller{ listener: lconn, - clientCfgs: make(map[string]*NatHoleClientCfg), - sessions: make(map[string]*NatHoleSession), + clientCfgs: make(map[string]*ClientCfg), + sessions: make(map[string]*Session), } return nc, nil } -func (nc *NatHoleController) ListenClient(name string, sk string) (sidCh chan *SidRequest) { - clientCfg := &NatHoleClientCfg{ +func (nc *Controller) ListenClient(name string, sk string) (sidCh chan *SidRequest) { + clientCfg := &ClientCfg{ Name: name, Sk: sk, SidCh: make(chan *SidRequest), @@ -61,13 +61,13 @@ func (nc *NatHoleController) ListenClient(name string, sk string) (sidCh chan *S return clientCfg.SidCh } -func (nc *NatHoleController) CloseClient(name string) { +func (nc *Controller) CloseClient(name string) { nc.mu.Lock() defer nc.mu.Unlock() delete(nc.clientCfgs, name) } -func (nc *NatHoleController) Run() { +func (nc *Controller) Run() { for { buf := pool.GetBuf(1024) n, raddr, err := nc.listener.ReadFromUDP(buf) @@ -96,15 +96,15 @@ func (nc *NatHoleController) Run() { } } -func (nc *NatHoleController) GenSid() string { +func (nc *Controller) GenSid() string { t := time.Now().Unix() - id, _ := util.RandId() + id, _ := util.RandID() return fmt.Sprintf("%d%s", t, id) } -func (nc *NatHoleController) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) { +func (nc *Controller) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) { sid := nc.GenSid() - session := &NatHoleSession{ + session := &Session{ Sid: sid, VisitorAddr: raddr, NotifyCh: make(chan struct{}, 0), @@ -157,7 +157,7 @@ func (nc *NatHoleController) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDP } } -func (nc *NatHoleController) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAddr) { +func (nc *Controller) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAddr) { nc.mu.RLock() session, ok := nc.sessions[m.Sid] nc.mu.RUnlock() @@ -172,7 +172,7 @@ func (nc *NatHoleController) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAd nc.listener.WriteToUDP(resp, raddr) } -func (nc *NatHoleController) GenNatHoleResponse(session *NatHoleSession, errInfo string) []byte { +func (nc *Controller) GenNatHoleResponse(session *Session, errInfo string) []byte { var ( sid string visitorAddr string @@ -197,7 +197,7 @@ func (nc *NatHoleController) GenNatHoleResponse(session *NatHoleSession, errInfo return b.Bytes() } -type NatHoleSession struct { +type Session struct { Sid string VisitorAddr *net.UDPAddr ClientAddr *net.UDPAddr @@ -205,7 +205,7 @@ type NatHoleSession struct { NotifyCh chan struct{} } -type NatHoleClientCfg struct { +type ClientCfg struct { Name string Sk string SidCh chan *SidRequest diff --git a/models/plugin/client/http_proxy.go b/models/plugin/client/http_proxy.go index b0ca9231..08600820 100644 --- a/models/plugin/client/http_proxy.go +++ b/models/plugin/client/http_proxy.go @@ -28,25 +28,25 @@ import ( gnet "github.com/fatedier/golib/net" ) -const PluginHttpProxy = "http_proxy" +const PluginHTTPProxy = "http_proxy" func init() { - Register(PluginHttpProxy, NewHttpProxyPlugin) + Register(PluginHTTPProxy, NewHTTPProxyPlugin) } -type HttpProxy struct { +type HTTPProxy struct { l *Listener s *http.Server AuthUser string AuthPasswd string } -func NewHttpProxyPlugin(params map[string]string) (Plugin, error) { +func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) { user := params["plugin_http_user"] passwd := params["plugin_http_passwd"] listener := NewProxyListener() - hp := &HttpProxy{ + hp := &HTTPProxy{ l: listener, AuthUser: user, AuthPasswd: passwd, @@ -60,11 +60,11 @@ func NewHttpProxyPlugin(params map[string]string) (Plugin, error) { return hp, nil } -func (hp *HttpProxy) Name() string { - return PluginHttpProxy +func (hp *HTTPProxy) Name() string { + return PluginHTTPProxy } -func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { +func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) sc, rd := gnet.NewSharedConn(wrapConn) @@ -90,13 +90,13 @@ func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBuf return } -func (hp *HttpProxy) Close() error { +func (hp *HTTPProxy) Close() error { hp.s.Close() hp.l.Close() return nil } -func (hp *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { +func (hp *HTTPProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if ok := hp.Auth(req); !ok { rw.Header().Set("Proxy-Authenticate", "Basic") rw.WriteHeader(http.StatusProxyAuthRequired) @@ -108,11 +108,11 @@ func (hp *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Connect request is handled in Handle function. hp.ConnectHandler(rw, req) } else { - hp.HttpHandler(rw, req) + hp.HTTPHandler(rw, req) } } -func (hp *HttpProxy) HttpHandler(rw http.ResponseWriter, req *http.Request) { +func (hp *HTTPProxy) HTTPHandler(rw http.ResponseWriter, req *http.Request) { removeProxyHeaders(req) resp, err := http.DefaultTransport.RoundTrip(req) @@ -134,7 +134,7 @@ func (hp *HttpProxy) HttpHandler(rw http.ResponseWriter, req *http.Request) { // deprecated // Hijack needs to SetReadDeadline on the Conn of the request, but if we use stream compression here, // we may always get i/o timeout error. -func (hp *HttpProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) { +func (hp *HTTPProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) { hj, ok := rw.(http.Hijacker) if !ok { rw.WriteHeader(http.StatusInternalServerError) @@ -158,7 +158,7 @@ func (hp *HttpProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) { go frpIo.Join(remote, client) } -func (hp *HttpProxy) Auth(req *http.Request) bool { +func (hp *HTTPProxy) Auth(req *http.Request) bool { if hp.AuthUser == "" && hp.AuthPasswd == "" { return true } @@ -184,7 +184,7 @@ func (hp *HttpProxy) Auth(req *http.Request) bool { return true } -func (hp *HttpProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser) { +func (hp *HTTPProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser) { defer rwc.Close() if ok := hp.Auth(req); !ok { res := getBadResponse() @@ -231,6 +231,7 @@ func removeProxyHeaders(req *http.Request) { func getBadResponse() *http.Response { header := make(map[string][]string) header["Proxy-Authenticate"] = []string{"Basic"} + header["Connection"] = []string{"close"} res := &http.Response{ Status: "407 Not authorized", StatusCode: 407, diff --git a/models/plugin/client/static_file.go b/models/plugin/client/static_file.go index 32325317..668a0338 100644 --- a/models/plugin/client/static_file.go +++ b/models/plugin/client/static_file.go @@ -64,8 +64,8 @@ func NewStaticFilePlugin(params map[string]string) (Plugin, error) { } router := mux.NewRouter() - router.Use(frpNet.NewHttpAuthMiddleware(httpUser, httpPasswd).Middleware) - router.PathPrefix(prefix).Handler(frpNet.MakeHttpGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))))).Methods("GET") + router.Use(frpNet.NewHTTPAuthMiddleware(httpUser, httpPasswd).Middleware) + router.PathPrefix(prefix).Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))))).Methods("GET") sp.s = &http.Server{ Handler: router, } diff --git a/models/plugin/server/manager.go b/models/plugin/server/manager.go index 559dae9e..dd2b67f4 100644 --- a/models/plugin/server/manager.go +++ b/models/plugin/server/manager.go @@ -52,7 +52,7 @@ func (m *Manager) Register(p Plugin) { m.pingPlugins = append(m.pingPlugins, p) } if p.IsSupport(OpNewWorkConn) { - m.pingPlugins = append(m.pingPlugins, p) + m.newWorkConnPlugins = append(m.newWorkConnPlugins, p) } if p.IsSupport(OpNewUserConn) { m.newUserConnPlugins = append(m.newUserConnPlugins, p) @@ -72,7 +72,7 @@ func (m *Manager) Login(content *LoginContent) (*LoginContent, error) { retContent interface{} err error ) - reqid, _ := util.RandId() + reqid, _ := util.RandID() xl := xlog.New().AppendPrefix("reqid: " + reqid) ctx := xlog.NewContext(context.Background(), xl) ctx = NewReqidContext(ctx, reqid) @@ -106,7 +106,7 @@ func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) { retContent interface{} err error ) - reqid, _ := util.RandId() + reqid, _ := util.RandID() xl := xlog.New().AppendPrefix("reqid: " + reqid) ctx := xlog.NewContext(context.Background(), xl) ctx = NewReqidContext(ctx, reqid) @@ -140,7 +140,7 @@ func (m *Manager) Ping(content *PingContent) (*PingContent, error) { retContent interface{} err error ) - reqid, _ := util.RandId() + reqid, _ := util.RandID() xl := xlog.New().AppendPrefix("reqid: " + reqid) ctx := xlog.NewContext(context.Background(), xl) ctx = NewReqidContext(ctx, reqid) @@ -174,7 +174,7 @@ func (m *Manager) NewWorkConn(content *NewWorkConnContent) (*NewWorkConnContent, retContent interface{} err error ) - reqid, _ := util.RandId() + reqid, _ := util.RandID() xl := xlog.New().AppendPrefix("reqid: " + reqid) ctx := xlog.NewContext(context.Background(), xl) ctx = NewReqidContext(ctx, reqid) @@ -208,7 +208,7 @@ func (m *Manager) NewUserConn(content *NewUserConnContent) (*NewUserConnContent, retContent interface{} err error ) - reqid, _ := util.RandId() + reqid, _ := util.RandID() xl := xlog.New().AppendPrefix("reqid: " + reqid) ctx := xlog.NewContext(context.Background(), xl) ctx = NewReqidContext(ctx, reqid) diff --git a/models/plugin/server/types.go b/models/plugin/server/types.go index 912a351c..5667625b 100644 --- a/models/plugin/server/types.go +++ b/models/plugin/server/types.go @@ -38,7 +38,7 @@ type LoginContent struct { type UserInfo struct { User string `json:"user"` Metas map[string]string `json:"metas"` - RunId string `json:"run_id"` + RunID string `json:"run_id"` } type NewProxyContent struct { diff --git a/models/proto/udp/udp.go b/models/proto/udp/udp.go index 8ae1db64..5a058964 100644 --- a/models/proto/udp/udp.go +++ b/models/proto/udp/udp.go @@ -26,20 +26,20 @@ import ( "github.com/fatedier/golib/pool" ) -func NewUdpPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UdpPacket { - return &msg.UdpPacket{ +func NewUDPPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UDPPacket { + return &msg.UDPPacket{ Content: base64.StdEncoding.EncodeToString(buf), LocalAddr: laddr, RemoteAddr: raddr, } } -func GetContent(m *msg.UdpPacket) (buf []byte, err error) { +func GetContent(m *msg.UDPPacket) (buf []byte, err error) { buf, err = base64.StdEncoding.DecodeString(m.Content) return } -func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh chan<- *msg.UdpPacket) { +func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UDPPacket, sendCh chan<- *msg.UDPPacket, bufSize int) { // read go func() { for udpMsg := range readCh { @@ -52,7 +52,7 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh }() // write - buf := pool.GetBuf(1500) + buf := pool.GetBuf(bufSize) defer pool.PutBuf(buf) for { n, remoteAddr, err := udpConn.ReadFromUDP(buf) @@ -60,7 +60,7 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh return } // buf[:n] will be encoded to string, so the bytes can be reused - udpMsg := NewUdpPacket(buf[:n], nil, remoteAddr) + udpMsg := NewUDPPacket(buf[:n], nil, remoteAddr) select { case sendCh <- udpMsg: @@ -69,7 +69,7 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh } } -func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- msg.Message) { +func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- msg.Message, bufSize int) { var ( mu sync.RWMutex ) @@ -85,7 +85,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- udpConn.Close() }() - buf := pool.GetBuf(1500) + buf := pool.GetBuf(bufSize) for { udpConn.SetReadDeadline(time.Now().Add(30 * time.Second)) n, _, err := udpConn.ReadFromUDP(buf) @@ -93,7 +93,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- return } - udpMsg := NewUdpPacket(buf[:n], nil, raddr) + udpMsg := NewUDPPacket(buf[:n], nil, raddr) if err = errors.PanicToError(func() { select { case sendCh <- udpMsg: diff --git a/models/proto/udp/udp_test.go b/models/proto/udp/udp_test.go index 6f364b8e..0e61d9e6 100644 --- a/models/proto/udp/udp_test.go +++ b/models/proto/udp/udp_test.go @@ -10,7 +10,7 @@ func TestUdpPacket(t *testing.T) { assert := assert.New(t) buf := []byte("hello world") - udpMsg := NewUdpPacket(buf, nil, nil) + udpMsg := NewUDPPacket(buf, nil, nil) newBuf, err := GetContent(udpMsg) assert.NoError(err) diff --git a/models/transport/tls.go b/models/transport/tls.go new file mode 100644 index 00000000..9338b90d --- /dev/null +++ b/models/transport/tls.go @@ -0,0 +1,136 @@ +package transport + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "io/ioutil" + "math/big" +) + +/* + Example for self-signed certificates by openssl: + + Self CA: + openssl genrsa -out ca.key 2048 + openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt + + Server: + openssl genrsa -out server.key 2048 + openssl req -new -key server.key -subj "/CN=example.server.com" -out server.csr + openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000 + + Client: + openssl genrsa -out client.key 2048 + openssl req -new -key client.key -subj "/CN=example.client.com" -out client.csr + openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 5000 + +*/ + +func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) { + tlsCert, err := tls.LoadX509KeyPair(certfile, keyfile) + if err != nil { + return nil, err + } + return &tlsCert, nil +} + +func newRandomTLSKeyPair() *tls.Certificate { + 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 &tlsCert +} + +// Only supprt one ca file to add +func newCertPool(caPath string) (*x509.CertPool, error) { + pool := x509.NewCertPool() + + caCrt, err := ioutil.ReadFile(caPath) + if err != nil { + return nil, err + } + + pool.AppendCertsFromPEM(caCrt) + + return pool, nil +} + +func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) { + var base = &tls.Config{} + + if certPath == "" || keyPath == "" { + // server will generate tls conf by itself + cert := newRandomTLSKeyPair() + base.Certificates = []tls.Certificate{*cert} + } else { + cert, err := newCustomTLSKeyPair(certPath, keyPath) + if err != nil { + return nil, err + } + + base.Certificates = []tls.Certificate{*cert} + } + + if caPath != "" { + pool, err := newCertPool(caPath) + if err != nil { + return nil, err + } + + base.ClientAuth = tls.RequireAndVerifyClientCert + base.ClientCAs = pool + } + + return base, nil +} + +func NewClientTLSConfig(certPath, keyPath, caPath, servearName string) (*tls.Config, error) { + var base = &tls.Config{} + + if certPath == "" || keyPath == "" { + // client will not generate tls conf by itself + } else { + cert, err := newCustomTLSKeyPair(certPath, keyPath) + if err != nil { + return nil, err + } + + base.Certificates = []tls.Certificate{*cert} + } + + if caPath != "" { + pool, err := newCertPool(caPath) + if err != nil { + return nil, err + } + + base.RootCAs = pool + base.ServerName = servearName + base.InsecureSkipVerify = false + } else { + base.InsecureSkipVerify = true + } + + return base, nil +} diff --git a/server/control.go b/server/control.go index 017ac390..e148459b 100644 --- a/server/control.go +++ b/server/control.go @@ -43,42 +43,42 @@ import ( type ControlManager struct { // controls indexed by run id - ctlsByRunId map[string]*Control + ctlsByRunID map[string]*Control mu sync.RWMutex } func NewControlManager() *ControlManager { return &ControlManager{ - ctlsByRunId: make(map[string]*Control), + ctlsByRunID: make(map[string]*Control), } } -func (cm *ControlManager) Add(runId string, ctl *Control) (oldCtl *Control) { +func (cm *ControlManager) Add(runID string, ctl *Control) (oldCtl *Control) { cm.mu.Lock() defer cm.mu.Unlock() - oldCtl, ok := cm.ctlsByRunId[runId] + oldCtl, ok := cm.ctlsByRunID[runID] if ok { oldCtl.Replaced(ctl) } - cm.ctlsByRunId[runId] = ctl + cm.ctlsByRunID[runID] = ctl return } // we should make sure if it's the same control to prevent delete a new one -func (cm *ControlManager) Del(runId string, ctl *Control) { +func (cm *ControlManager) Del(runID string, ctl *Control) { cm.mu.Lock() defer cm.mu.Unlock() - if c, ok := cm.ctlsByRunId[runId]; ok && c == ctl { - delete(cm.ctlsByRunId, runId) + if c, ok := cm.ctlsByRunID[runID]; ok && c == ctl { + delete(cm.ctlsByRunID, runID) } } -func (cm *ControlManager) GetById(runId string) (ctl *Control, ok bool) { +func (cm *ControlManager) GetByID(runID string) (ctl *Control, ok bool) { cm.mu.RLock() defer cm.mu.RUnlock() - ctl, ok = cm.ctlsByRunId[runId] + ctl, ok = cm.ctlsByRunID[runID] return } @@ -87,7 +87,7 @@ type Control struct { rc *controller.ResourceController // proxy manager - pxyManager *proxy.ProxyManager + pxyManager *proxy.Manager // plugin manager pluginManager *plugin.Manager @@ -125,7 +125,7 @@ type Control struct { // A new run id will be generated when a new client login. // If run id got from login message has same run id, it means it's the same client, so we can // replace old controller instantly. - runId string + runID string // control status status string @@ -147,7 +147,7 @@ type Control struct { func NewControl( ctx context.Context, rc *controller.ResourceController, - pxyManager *proxy.ProxyManager, + pxyManager *proxy.Manager, pluginManager *plugin.Manager, authVerifier auth.Verifier, ctlConn net.Conn, @@ -173,7 +173,7 @@ func NewControl( poolCount: poolCount, portsUsedNum: 0, lastPing: time.Now(), - runId: loginMsg.RunId, + runID: loginMsg.RunID, status: consts.Working, readerShutdown: shutdown.New(), writerShutdown: shutdown.New(), @@ -189,8 +189,8 @@ func NewControl( func (ctl *Control) Start() { loginRespMsg := &msg.LoginResp{ Version: version.Full(), - RunId: ctl.runId, - ServerUdpPort: ctl.serverCfg.BindUdpPort, + RunID: ctl.runID, + ServerUDPPort: ctl.serverCfg.BindUDPPort, Error: "", } msg.WriteMsg(ctl.conn, loginRespMsg) @@ -280,8 +280,8 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) { func (ctl *Control) Replaced(newCtl *Control) { xl := ctl.xl - xl.Info("Replaced by client [%s]", newCtl.runId) - ctl.runId = "" + xl.Info("Replaced by client [%s]", newCtl.runID) + ctl.runID = "" ctl.allShutdown.Start() } @@ -304,14 +304,15 @@ func (ctl *Control) writer() { return } for { - if m, ok := <-ctl.sendCh; !ok { + m, ok := <-ctl.sendCh + if !ok { xl.Info("control writer is closing") return - } else { - if err := msg.WriteMsg(encWriter, m); err != nil { - xl.Warn("write message to control connection error: %v", err) - return - } + } + + if err := msg.WriteMsg(encWriter, m); err != nil { + xl.Warn("write message to control connection error: %v", err) + return } } } @@ -330,18 +331,18 @@ func (ctl *Control) reader() { encReader := crypto.NewReader(ctl.conn, []byte(ctl.serverCfg.Token)) for { - if m, err := msg.ReadMsg(encReader); err != nil { + m, err := msg.ReadMsg(encReader) + if err != nil { if err == io.EOF { xl.Debug("control connection closed") return - } else { - xl.Warn("read error: %v", err) - ctl.conn.Close() - return } - } else { - ctl.readCh <- m + xl.Warn("read error: %v", err) + ctl.conn.Close() + return } + + ctl.readCh <- m } } @@ -422,7 +423,7 @@ func (ctl *Control) manager() { User: plugin.UserInfo{ User: ctl.loginMsg.User, Metas: ctl.loginMsg.Metas, - RunId: ctl.loginMsg.RunId, + RunID: ctl.loginMsg.RunID, }, NewProxy: *m, } @@ -454,7 +455,7 @@ func (ctl *Control) manager() { User: plugin.UserInfo{ User: ctl.loginMsg.User, Metas: ctl.loginMsg.Metas, - RunId: ctl.loginMsg.RunId, + RunID: ctl.loginMsg.RunID, }, Ping: *m, } @@ -490,7 +491,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err userInfo := plugin.UserInfo{ User: ctl.loginMsg.User, Metas: ctl.loginMsg.Metas, - RunId: ctl.runId, + RunID: ctl.runID, } // NewProxy will return a interface Proxy. diff --git a/server/controller/resource.go b/server/controller/resource.go index b0c6ea2e..6e128b8c 100644 --- a/server/controller/resource.go +++ b/server/controller/resource.go @@ -19,6 +19,7 @@ import ( plugin "github.com/fatedier/frp/models/plugin/server" "github.com/fatedier/frp/server/group" "github.com/fatedier/frp/server/ports" + "github.com/fatedier/frp/server/visitor" "github.com/fatedier/frp/utils/tcpmux" "github.com/fatedier/frp/utils/vhost" ) @@ -26,34 +27,34 @@ import ( // All resource managers and controllers type ResourceController struct { // Manage all visitor listeners - VisitorManager *VisitorManager + VisitorManager *visitor.Manager - // Tcp Group Controller - TcpGroupCtl *group.TcpGroupCtl + // TCP Group Controller + TCPGroupCtl *group.TCPGroupCtl // HTTP Group Controller HTTPGroupCtl *group.HTTPGroupController // TCP Mux Group Controller - TcpMuxGroupCtl *group.TcpMuxGroupCtl + TCPMuxGroupCtl *group.TCPMuxGroupCtl - // Manage all tcp ports - TcpPortManager *ports.PortManager + // Manage all TCP ports + TCPPortManager *ports.Manager - // Manage all udp ports - UdpPortManager *ports.PortManager + // Manage all UDP ports + UDPPortManager *ports.Manager - // For http proxies, forwarding http requests - HttpReverseProxy *vhost.HttpReverseProxy + // For HTTP proxies, forwarding HTTP requests + HTTPReverseProxy *vhost.HTTPReverseProxy - // For https proxies, route requests to different clients by hostname and other information - VhostHttpsMuxer *vhost.HttpsMuxer + // For HTTPS proxies, route requests to different clients by hostname and other information + VhostHTTPSMuxer *vhost.HTTPSMuxer // Controller for nat hole connections - NatHoleController *nathole.NatHoleController + NatHoleController *nathole.Controller - // TcpMux HTTP CONNECT multiplexer - TcpMuxHttpConnectMuxer *tcpmux.HttpConnectTcpMuxer + // TCPMux HTTP CONNECT multiplexer + TCPMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer // All server manager plugin PluginManager *plugin.Manager diff --git a/server/dashboard.go b/server/dashboard.go index 94a36680..473f37e4 100644 --- a/server/dashboard.go +++ b/server/dashboard.go @@ -37,7 +37,7 @@ func (svr *Service) RunDashboardServer(addr string, port int) (err error) { router := mux.NewRouter() user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd - router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware) + router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) // metrics if svr.cfg.EnablePrometheus { @@ -45,14 +45,14 @@ func (svr *Service) RunDashboardServer(addr string, port int) (err error) { } // api, see dashboard_api.go - router.HandleFunc("/api/serverinfo", svr.ApiServerInfo).Methods("GET") - router.HandleFunc("/api/proxy/{type}", svr.ApiProxyByType).Methods("GET") - router.HandleFunc("/api/proxy/{type}/{name}", svr.ApiProxyByTypeAndName).Methods("GET") - router.HandleFunc("/api/traffic/{name}", svr.ApiProxyTraffic).Methods("GET") + router.HandleFunc("/api/serverinfo", svr.APIServerInfo).Methods("GET") + router.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET") + router.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET") + router.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET") // view router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") - router.PathPrefix("/static/").Handler(frpNet.MakeHttpGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") + router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/static/", http.StatusMovedPermanently) diff --git a/server/dashboard_api.go b/server/dashboard_api.go index 89d8d8a4..e0b10ced 100644 --- a/server/dashboard_api.go +++ b/server/dashboard_api.go @@ -32,13 +32,13 @@ type GeneralResponse struct { Msg string } -type ServerInfoResp struct { +type serverInfoResp struct { Version string `json:"version"` BindPort int `json:"bind_port"` - BindUdpPort int `json:"bind_udp_port"` - VhostHttpPort int `json:"vhost_http_port"` - VhostHttpsPort int `json:"vhost_https_port"` - KcpBindPort int `json:"kcp_bind_port"` + BindUDPPort int `json:"bind_udp_port"` + VhostHTTPPort int `json:"vhost_http_port"` + VhostHTTPSPort int `json:"vhost_https_port"` + KCPBindPort int `json:"kcp_bind_port"` SubdomainHost string `json:"subdomain_host"` MaxPoolCount int64 `json:"max_pool_count"` MaxPortsPerClient int64 `json:"max_ports_per_client"` @@ -52,7 +52,7 @@ type ServerInfoResp struct { } // api/serverinfo -func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) { +func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} defer func() { log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code) @@ -64,13 +64,13 @@ func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) { log.Info("Http request: [%s]", r.URL.Path) serverStats := mem.StatsCollector.GetServer() - svrResp := ServerInfoResp{ + svrResp := serverInfoResp{ Version: version.Full(), BindPort: svr.cfg.BindPort, - BindUdpPort: svr.cfg.BindUdpPort, - VhostHttpPort: svr.cfg.VhostHttpPort, - VhostHttpsPort: svr.cfg.VhostHttpsPort, - KcpBindPort: svr.cfg.KcpBindPort, + BindUDPPort: svr.cfg.BindUDPPort, + VhostHTTPPort: svr.cfg.VhostHTTPPort, + VhostHTTPSPort: svr.cfg.VhostHTTPSPort, + KCPBindPort: svr.cfg.KCPBindPort, SubdomainHost: svr.cfg.SubDomainHost, MaxPoolCount: svr.cfg.MaxPoolCount, MaxPortsPerClient: svr.cfg.MaxPortsPerClient, @@ -91,58 +91,58 @@ type BaseOutConf struct { config.BaseProxyConf } -type TcpOutConf struct { +type TCPOutConf struct { BaseOutConf RemotePort int `json:"remote_port"` } -type TcpMuxOutConf struct { +type TCPMuxOutConf struct { BaseOutConf config.DomainConf Multiplexer string `json:"multiplexer"` } -type UdpOutConf struct { +type UDPOutConf struct { BaseOutConf RemotePort int `json:"remote_port"` } -type HttpOutConf struct { +type HTTPOutConf struct { BaseOutConf config.DomainConf Locations []string `json:"locations"` HostHeaderRewrite string `json:"host_header_rewrite"` } -type HttpsOutConf struct { +type HTTPSOutConf struct { BaseOutConf config.DomainConf } -type StcpOutConf struct { +type STCPOutConf struct { BaseOutConf } -type XtcpOutConf struct { +type XTCPOutConf struct { BaseOutConf } func getConfByType(proxyType string) interface{} { switch proxyType { - case consts.TcpProxy: - return &TcpOutConf{} - case consts.TcpMuxProxy: - return &TcpMuxOutConf{} - case consts.UdpProxy: - return &UdpOutConf{} - case consts.HttpProxy: - return &HttpOutConf{} - case consts.HttpsProxy: - return &HttpsOutConf{} - case consts.StcpProxy: - return &StcpOutConf{} - case consts.XtcpProxy: - return &XtcpOutConf{} + case consts.TCPProxy: + return &TCPOutConf{} + case consts.TCPMuxProxy: + return &TCPMuxOutConf{} + case consts.UDPProxy: + return &UDPOutConf{} + case consts.HTTPProxy: + return &HTTPOutConf{} + case consts.HTTPSProxy: + return &HTTPSOutConf{} + case consts.STCPProxy: + return &STCPOutConf{} + case consts.XTCPProxy: + return &XTCPOutConf{} default: return nil } @@ -165,7 +165,7 @@ type GetProxyInfoResp struct { } // api/proxy/:type -func (svr *Service) ApiProxyByType(w http.ResponseWriter, r *http.Request) { +func (svr *Service) APIProxyByType(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) proxyType := params["type"] @@ -230,7 +230,7 @@ type GetProxyStatsResp struct { } // api/proxy/:type/:name -func (svr *Service) ApiProxyByTypeAndName(w http.ResponseWriter, r *http.Request) { +func (svr *Service) APIProxyByTypeAndName(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) proxyType := params["type"] @@ -299,7 +299,7 @@ type GetProxyTrafficResp struct { TrafficOut []int64 `json:"traffic_out"` } -func (svr *Service) ApiProxyTraffic(w http.ResponseWriter, r *http.Request) { +func (svr *Service) APIProxyTraffic(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) name := params["name"] @@ -321,10 +321,9 @@ func (svr *Service) ApiProxyTraffic(w http.ResponseWriter, r *http.Request) { res.Code = 404 res.Msg = "no proxy info found" return - } else { - trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn - trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut } + trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn + trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut buf, _ := json.Marshal(&trafficResp) res.Msg = string(buf) diff --git a/server/group/http.go b/server/group/http.go index 31609680..90b82292 100644 --- a/server/group/http.go +++ b/server/group/http.go @@ -12,12 +12,12 @@ import ( type HTTPGroupController struct { groups map[string]*HTTPGroup - vhostRouter *vhost.VhostRouters + vhostRouter *vhost.Routers mu sync.Mutex } -func NewHTTPGroupController(vhostRouter *vhost.VhostRouters) *HTTPGroupController { +func NewHTTPGroupController(vhostRouter *vhost.Routers) *HTTPGroupController { return &HTTPGroupController{ groups: make(map[string]*HTTPGroup), vhostRouter: vhostRouter, @@ -25,7 +25,7 @@ func NewHTTPGroupController(vhostRouter *vhost.VhostRouters) *HTTPGroupControlle } func (ctl *HTTPGroupController) Register(proxyName, group, groupKey string, - routeConfig vhost.VhostRouteConfig) (err error) { + routeConfig vhost.RouteConfig) (err error) { indexKey := httpGroupIndex(group, routeConfig.Domain, routeConfig.Location) ctl.mu.Lock() @@ -76,7 +76,7 @@ func NewHTTPGroup(ctl *HTTPGroupController) *HTTPGroup { } func (g *HTTPGroup) Register(proxyName, group, groupKey string, - routeConfig vhost.VhostRouteConfig) (err error) { + routeConfig vhost.RouteConfig) (err error) { g.mu.Lock() defer g.mu.Unlock() diff --git a/server/group/tcp.go b/server/group/tcp.go index 9c027b94..0128482f 100644 --- a/server/group/tcp.go +++ b/server/group/tcp.go @@ -24,32 +24,32 @@ import ( gerr "github.com/fatedier/golib/errors" ) -// TcpGroupCtl manage all TcpGroups -type TcpGroupCtl struct { - groups map[string]*TcpGroup +// TCPGroupCtl manage all TCPGroups +type TCPGroupCtl struct { + groups map[string]*TCPGroup // portManager is used to manage port - portManager *ports.PortManager + portManager *ports.Manager mu sync.Mutex } -// NewTcpGroupCtl return a new TcpGroupCtl -func NewTcpGroupCtl(portManager *ports.PortManager) *TcpGroupCtl { - return &TcpGroupCtl{ - groups: make(map[string]*TcpGroup), +// NewTCPGroupCtl return a new TcpGroupCtl +func NewTCPGroupCtl(portManager *ports.Manager) *TCPGroupCtl { + return &TCPGroupCtl{ + groups: make(map[string]*TCPGroup), portManager: portManager, } } -// Listen is the wrapper for TcpGroup's Listen +// Listen is the wrapper for TCPGroup's Listen // If there are no group, we will create one here -func (tgc *TcpGroupCtl) Listen(proxyName string, group string, groupKey string, +func (tgc *TCPGroupCtl) Listen(proxyName string, group string, groupKey string, addr string, port int) (l net.Listener, realPort int, err error) { tgc.mu.Lock() tcpGroup, ok := tgc.groups[group] if !ok { - tcpGroup = NewTcpGroup(tgc) + tcpGroup = NewTCPGroup(tgc) tgc.groups[group] = tcpGroup } tgc.mu.Unlock() @@ -57,15 +57,15 @@ func (tgc *TcpGroupCtl) Listen(proxyName string, group string, groupKey string, return tcpGroup.Listen(proxyName, group, groupKey, addr, port) } -// RemoveGroup remove TcpGroup from controller -func (tgc *TcpGroupCtl) RemoveGroup(group string) { +// RemoveGroup remove TCPGroup from controller +func (tgc *TCPGroupCtl) RemoveGroup(group string) { tgc.mu.Lock() defer tgc.mu.Unlock() delete(tgc.groups, group) } -// TcpGroup route connections to different proxies -type TcpGroup struct { +// TCPGroup route connections to different proxies +type TCPGroup struct { group string groupKey string addr string @@ -75,24 +75,24 @@ type TcpGroup struct { acceptCh chan net.Conn index uint64 tcpLn net.Listener - lns []*TcpGroupListener - ctl *TcpGroupCtl + lns []*TCPGroupListener + ctl *TCPGroupCtl mu sync.Mutex } -// NewTcpGroup return a new TcpGroup -func NewTcpGroup(ctl *TcpGroupCtl) *TcpGroup { - return &TcpGroup{ - lns: make([]*TcpGroupListener, 0), +// NewTCPGroup return a new TCPGroup +func NewTCPGroup(ctl *TCPGroupCtl) *TCPGroup { + return &TCPGroup{ + lns: make([]*TCPGroupListener, 0), ctl: ctl, acceptCh: make(chan net.Conn), } } -// Listen will return a new TcpGroupListener -// if TcpGroup already has a listener, just add a new TcpGroupListener to the queues +// Listen will return a new TCPGroupListener +// if TCPGroup already has a listener, just add a new TCPGroupListener to the queues // otherwise, listen on the real address -func (tg *TcpGroup) Listen(proxyName string, group string, groupKey string, addr string, port int) (ln *TcpGroupListener, realPort int, err error) { +func (tg *TCPGroup) Listen(proxyName string, group string, groupKey string, addr string, port int) (ln *TCPGroupListener, realPort int, err error) { tg.mu.Lock() defer tg.mu.Unlock() if len(tg.lns) == 0 { @@ -106,7 +106,7 @@ func (tg *TcpGroup) Listen(proxyName string, group string, groupKey string, addr err = errRet return } - ln = newTcpGroupListener(group, tg, tcpLn.Addr()) + ln = newTCPGroupListener(group, tg, tcpLn.Addr()) tg.group = group tg.groupKey = groupKey @@ -133,7 +133,7 @@ func (tg *TcpGroup) Listen(proxyName string, group string, groupKey string, addr err = ErrGroupAuthFailed return } - ln = newTcpGroupListener(group, tg, tg.lns[0].Addr()) + ln = newTCPGroupListener(group, tg, tg.lns[0].Addr()) realPort = tg.realPort tg.lns = append(tg.lns, ln) } @@ -141,7 +141,7 @@ func (tg *TcpGroup) Listen(proxyName string, group string, groupKey string, addr } // worker is called when the real tcp listener has been created -func (tg *TcpGroup) worker() { +func (tg *TCPGroup) worker() { for { c, err := tg.tcpLn.Accept() if err != nil { @@ -156,12 +156,12 @@ func (tg *TcpGroup) worker() { } } -func (tg *TcpGroup) Accept() <-chan net.Conn { +func (tg *TCPGroup) Accept() <-chan net.Conn { return tg.acceptCh } -// CloseListener remove the TcpGroupListener from the TcpGroup -func (tg *TcpGroup) CloseListener(ln *TcpGroupListener) { +// CloseListener remove the TCPGroupListener from the TCPGroup +func (tg *TCPGroup) CloseListener(ln *TCPGroupListener) { tg.mu.Lock() defer tg.mu.Unlock() for i, tmpLn := range tg.lns { @@ -178,17 +178,17 @@ func (tg *TcpGroup) CloseListener(ln *TcpGroupListener) { } } -// TcpGroupListener -type TcpGroupListener struct { +// TCPGroupListener +type TCPGroupListener struct { groupName string - group *TcpGroup + group *TCPGroup addr net.Addr closeCh chan struct{} } -func newTcpGroupListener(name string, group *TcpGroup, addr net.Addr) *TcpGroupListener { - return &TcpGroupListener{ +func newTCPGroupListener(name string, group *TCPGroup, addr net.Addr) *TCPGroupListener { + return &TCPGroupListener{ groupName: name, group: group, addr: addr, @@ -196,8 +196,8 @@ func newTcpGroupListener(name string, group *TcpGroup, addr net.Addr) *TcpGroupL } } -// Accept will accept connections from TcpGroup -func (ln *TcpGroupListener) Accept() (c net.Conn, err error) { +// Accept will accept connections from TCPGroup +func (ln *TCPGroupListener) Accept() (c net.Conn, err error) { var ok bool select { case <-ln.closeCh: @@ -210,12 +210,12 @@ func (ln *TcpGroupListener) Accept() (c net.Conn, err error) { } } -func (ln *TcpGroupListener) Addr() net.Addr { +func (ln *TCPGroupListener) Addr() net.Addr { return ln.addr } // Close close the listener -func (ln *TcpGroupListener) Close() (err error) { +func (ln *TCPGroupListener) Close() (err error) { close(ln.closeCh) // remove self from TcpGroup diff --git a/server/group/tcpmux.go b/server/group/tcpmux.go index 4cae5b80..61058ca6 100644 --- a/server/group/tcpmux.go +++ b/server/group/tcpmux.go @@ -27,53 +27,54 @@ import ( gerr "github.com/fatedier/golib/errors" ) -// TcpMuxGroupCtl manage all TcpMuxGroups -type TcpMuxGroupCtl struct { - groups map[string]*TcpMuxGroup +// TCPMuxGroupCtl manage all TCPMuxGroups +type TCPMuxGroupCtl struct { + groups map[string]*TCPMuxGroup // portManager is used to manage port - tcpMuxHttpConnectMuxer *tcpmux.HttpConnectTcpMuxer + tcpMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer mu sync.Mutex } -// NewTcpMuxGroupCtl return a new TcpMuxGroupCtl -func NewTcpMuxGroupCtl(tcpMuxHttpConnectMuxer *tcpmux.HttpConnectTcpMuxer) *TcpMuxGroupCtl { - return &TcpMuxGroupCtl{ - groups: make(map[string]*TcpMuxGroup), - tcpMuxHttpConnectMuxer: tcpMuxHttpConnectMuxer, +// NewTCPMuxGroupCtl return a new TCPMuxGroupCtl +func NewTCPMuxGroupCtl(tcpMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer) *TCPMuxGroupCtl { + return &TCPMuxGroupCtl{ + groups: make(map[string]*TCPMuxGroup), + tcpMuxHTTPConnectMuxer: tcpMuxHTTPConnectMuxer, } } -// Listen is the wrapper for TcpMuxGroup's Listen +// Listen is the wrapper for TCPMuxGroup's Listen // If there are no group, we will create one here -func (tmgc *TcpMuxGroupCtl) Listen(multiplexer string, group string, groupKey string, - domain string, ctx context.Context) (l net.Listener, err error) { +func (tmgc *TCPMuxGroupCtl) Listen(ctx context.Context, multiplexer string, group string, groupKey string, + domain string) (l net.Listener, err error) { + tmgc.mu.Lock() tcpMuxGroup, ok := tmgc.groups[group] if !ok { - tcpMuxGroup = NewTcpMuxGroup(tmgc) + tcpMuxGroup = NewTCPMuxGroup(tmgc) tmgc.groups[group] = tcpMuxGroup } tmgc.mu.Unlock() switch multiplexer { - case consts.HttpConnectTcpMultiplexer: - return tcpMuxGroup.HttpConnectListen(group, groupKey, domain, ctx) + case consts.HTTPConnectTCPMultiplexer: + return tcpMuxGroup.HTTPConnectListen(ctx, group, groupKey, domain) default: err = fmt.Errorf("unknown multiplexer [%s]", multiplexer) return } } -// RemoveGroup remove TcpMuxGroup from controller -func (tmgc *TcpMuxGroupCtl) RemoveGroup(group string) { +// RemoveGroup remove TCPMuxGroup from controller +func (tmgc *TCPMuxGroupCtl) RemoveGroup(group string) { tmgc.mu.Lock() defer tmgc.mu.Unlock() delete(tmgc.groups, group) } -// TcpMuxGroup route connections to different proxies -type TcpMuxGroup struct { +// TCPMuxGroup route connections to different proxies +type TCPMuxGroup struct { group string groupKey string domain string @@ -81,36 +82,36 @@ type TcpMuxGroup struct { acceptCh chan net.Conn index uint64 tcpMuxLn net.Listener - lns []*TcpMuxGroupListener - ctl *TcpMuxGroupCtl + lns []*TCPMuxGroupListener + ctl *TCPMuxGroupCtl mu sync.Mutex } -// NewTcpMuxGroup return a new TcpMuxGroup -func NewTcpMuxGroup(ctl *TcpMuxGroupCtl) *TcpMuxGroup { - return &TcpMuxGroup{ - lns: make([]*TcpMuxGroupListener, 0), +// NewTCPMuxGroup return a new TCPMuxGroup +func NewTCPMuxGroup(ctl *TCPMuxGroupCtl) *TCPMuxGroup { + return &TCPMuxGroup{ + lns: make([]*TCPMuxGroupListener, 0), ctl: ctl, acceptCh: make(chan net.Conn), } } -// Listen will return a new TcpMuxGroupListener -// if TcpMuxGroup already has a listener, just add a new TcpMuxGroupListener to the queues +// Listen will return a new TCPMuxGroupListener +// if TCPMuxGroup already has a listener, just add a new TCPMuxGroupListener to the queues // otherwise, listen on the real address -func (tmg *TcpMuxGroup) HttpConnectListen(group string, groupKey string, domain string, context context.Context) (ln *TcpMuxGroupListener, err error) { +func (tmg *TCPMuxGroup) HTTPConnectListen(ctx context.Context, group string, groupKey string, domain string) (ln *TCPMuxGroupListener, err error) { tmg.mu.Lock() defer tmg.mu.Unlock() if len(tmg.lns) == 0 { // the first listener, listen on the real address - routeConfig := &vhost.VhostRouteConfig{ + routeConfig := &vhost.RouteConfig{ Domain: domain, } - tcpMuxLn, errRet := tmg.ctl.tcpMuxHttpConnectMuxer.Listen(context, routeConfig) + tcpMuxLn, errRet := tmg.ctl.tcpMuxHTTPConnectMuxer.Listen(ctx, routeConfig) if errRet != nil { return nil, errRet } - ln = newTcpMuxGroupListener(group, tmg, tcpMuxLn.Addr()) + ln = newTCPMuxGroupListener(group, tmg, tcpMuxLn.Addr()) tmg.group = group tmg.groupKey = groupKey @@ -129,14 +130,14 @@ func (tmg *TcpMuxGroup) HttpConnectListen(group string, groupKey string, domain if tmg.groupKey != groupKey { return nil, ErrGroupAuthFailed } - ln = newTcpMuxGroupListener(group, tmg, tmg.lns[0].Addr()) + ln = newTCPMuxGroupListener(group, tmg, tmg.lns[0].Addr()) tmg.lns = append(tmg.lns, ln) } return } -// worker is called when the real tcp listener has been created -func (tmg *TcpMuxGroup) worker() { +// worker is called when the real TCP listener has been created +func (tmg *TCPMuxGroup) worker() { for { c, err := tmg.tcpMuxLn.Accept() if err != nil { @@ -151,12 +152,12 @@ func (tmg *TcpMuxGroup) worker() { } } -func (tmg *TcpMuxGroup) Accept() <-chan net.Conn { +func (tmg *TCPMuxGroup) Accept() <-chan net.Conn { return tmg.acceptCh } -// CloseListener remove the TcpMuxGroupListener from the TcpMuxGroup -func (tmg *TcpMuxGroup) CloseListener(ln *TcpMuxGroupListener) { +// CloseListener remove the TCPMuxGroupListener from the TCPMuxGroup +func (tmg *TCPMuxGroup) CloseListener(ln *TCPMuxGroupListener) { tmg.mu.Lock() defer tmg.mu.Unlock() for i, tmpLn := range tmg.lns { @@ -172,17 +173,17 @@ func (tmg *TcpMuxGroup) CloseListener(ln *TcpMuxGroupListener) { } } -// TcpMuxGroupListener -type TcpMuxGroupListener struct { +// TCPMuxGroupListener +type TCPMuxGroupListener struct { groupName string - group *TcpMuxGroup + group *TCPMuxGroup addr net.Addr closeCh chan struct{} } -func newTcpMuxGroupListener(name string, group *TcpMuxGroup, addr net.Addr) *TcpMuxGroupListener { - return &TcpMuxGroupListener{ +func newTCPMuxGroupListener(name string, group *TCPMuxGroup, addr net.Addr) *TCPMuxGroupListener { + return &TCPMuxGroupListener{ groupName: name, group: group, addr: addr, @@ -190,8 +191,8 @@ func newTcpMuxGroupListener(name string, group *TcpMuxGroup, addr net.Addr) *Tcp } } -// Accept will accept connections from TcpMuxGroup -func (ln *TcpMuxGroupListener) Accept() (c net.Conn, err error) { +// Accept will accept connections from TCPMuxGroup +func (ln *TCPMuxGroupListener) Accept() (c net.Conn, err error) { var ok bool select { case <-ln.closeCh: @@ -204,12 +205,12 @@ func (ln *TcpMuxGroupListener) Accept() (c net.Conn, err error) { } } -func (ln *TcpMuxGroupListener) Addr() net.Addr { +func (ln *TCPMuxGroupListener) Addr() net.Addr { return ln.addr } // Close close the listener -func (ln *TcpMuxGroupListener) Close() (err error) { +func (ln *TCPMuxGroupListener) Close() (err error) { close(ln.closeCh) // remove self from TcpMuxGroup diff --git a/server/ports/ports.go b/server/ports/ports.go index a42fd91c..1dabd450 100644 --- a/server/ports/ports.go +++ b/server/ports/ports.go @@ -29,7 +29,7 @@ type PortCtx struct { UpdateTime time.Time } -type PortManager struct { +type Manager struct { reservedPorts map[string]*PortCtx usedPorts map[int]*PortCtx freePorts map[int]struct{} @@ -39,8 +39,8 @@ type PortManager struct { mu sync.Mutex } -func NewPortManager(netType string, bindAddr string, allowPorts map[int]struct{}) *PortManager { - pm := &PortManager{ +func NewManager(netType string, bindAddr string, allowPorts map[int]struct{}) *Manager { + pm := &Manager{ reservedPorts: make(map[string]*PortCtx), usedPorts: make(map[int]*PortCtx), freePorts: make(map[int]struct{}), @@ -48,7 +48,7 @@ func NewPortManager(netType string, bindAddr string, allowPorts map[int]struct{} netType: netType, } if len(allowPorts) > 0 { - for port, _ := range allowPorts { + for port := range allowPorts { pm.freePorts[port] = struct{}{} } } else { @@ -60,7 +60,7 @@ func NewPortManager(netType string, bindAddr string, allowPorts map[int]struct{} return pm } -func (pm *PortManager) Acquire(name string, port int) (realPort int, err error) { +func (pm *Manager) Acquire(name string, port int) (realPort int, err error) { portCtx := &PortCtx{ ProxyName: name, Closed: false, @@ -94,7 +94,7 @@ func (pm *PortManager) Acquire(name string, port int) (realPort int, err error) // get random port count := 0 maxTryTimes := 5 - for k, _ := range pm.freePorts { + for k := range pm.freePorts { count++ if count > maxTryTimes { break @@ -132,7 +132,7 @@ func (pm *PortManager) Acquire(name string, port int) (realPort int, err error) return } -func (pm *PortManager) isPortAvailable(port int) bool { +func (pm *Manager) isPortAvailable(port int) bool { if pm.netType == "udp" { addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pm.bindAddr, port)) if err != nil { @@ -144,17 +144,17 @@ func (pm *PortManager) isPortAvailable(port int) bool { } l.Close() return true - } else { - l, err := net.Listen(pm.netType, fmt.Sprintf("%s:%d", pm.bindAddr, port)) - if err != nil { - return false - } - l.Close() - return true } + + l, err := net.Listen(pm.netType, fmt.Sprintf("%s:%d", pm.bindAddr, port)) + if err != nil { + return false + } + l.Close() + return true } -func (pm *PortManager) Release(port int) { +func (pm *Manager) Release(port int) { pm.mu.Lock() defer pm.mu.Unlock() if ctx, ok := pm.usedPorts[port]; ok { @@ -166,7 +166,7 @@ func (pm *PortManager) Release(port int) { } // Release reserved port if it isn't used in last 24 hours. -func (pm *PortManager) cleanReservedPortsWorker() { +func (pm *Manager) cleanReservedPortsWorker() { for { time.Sleep(CleanReservedPortsInterval) pm.mu.Lock() diff --git a/server/proxy/http.go b/server/proxy/http.go index 35f65cbd..ce14388e 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -28,20 +28,20 @@ import ( frpIo "github.com/fatedier/golib/io" ) -type HttpProxy struct { +type HTTPProxy struct { *BaseProxy - cfg *config.HttpProxyConf + cfg *config.HTTPProxyConf closeFuncs []func() } -func (pxy *HttpProxy) Run() (remoteAddr string, err error) { +func (pxy *HTTPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl - routeConfig := vhost.VhostRouteConfig{ + routeConfig := vhost.RouteConfig{ RewriteHost: pxy.cfg.HostHeaderRewrite, Headers: pxy.cfg.Headers, - Username: pxy.cfg.HttpUser, - Password: pxy.cfg.HttpPwd, + Username: pxy.cfg.HTTPUser, + Password: pxy.cfg.HTTPPwd, CreateConnFn: pxy.GetRealConn, } @@ -80,15 +80,15 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { }) } else { // no group - err = pxy.rc.HttpReverseProxy.Register(routeConfig) + err = pxy.rc.HTTPReverseProxy.Register(routeConfig) if err != nil { return } pxy.closeFuncs = append(pxy.closeFuncs, func() { - pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation) + pxy.rc.HTTPReverseProxy.UnRegister(tmpDomain, tmpLocation) }) } - addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpPort))) + addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHTTPPort))) xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group) } } @@ -111,15 +111,15 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.Group, tmpDomain, tmpLocation) }) } else { - err = pxy.rc.HttpReverseProxy.Register(routeConfig) + err = pxy.rc.HTTPReverseProxy.Register(routeConfig) if err != nil { return } pxy.closeFuncs = append(pxy.closeFuncs, func() { - pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation) + pxy.rc.HTTPReverseProxy.UnRegister(tmpDomain, tmpLocation) }) } - addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHttpPort)) + addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHTTPPort)) xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group) } @@ -128,11 +128,11 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) { return } -func (pxy *HttpProxy) GetConf() config.ProxyConf { +func (pxy *HTTPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) { +func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) { xl := pxy.xl rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr) if errRet != nil { @@ -163,7 +163,7 @@ func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err return } -func (pxy *HttpProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) { +func (pxy *HTTPProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) { name := pxy.GetName() proxyType := pxy.GetConf().GetBaseInfo().ProxyType metrics.Server.CloseConnection(name, proxyType) @@ -171,7 +171,7 @@ func (pxy *HttpProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) { metrics.Server.AddTrafficOut(name, proxyType, totalRead) } -func (pxy *HttpProxy) Close() { +func (pxy *HTTPProxy) Close() { pxy.BaseProxy.Close() for _, closeFn := range pxy.closeFuncs { closeFn() diff --git a/server/proxy/https.go b/server/proxy/https.go index 0191496f..4a1c999f 100644 --- a/server/proxy/https.go +++ b/server/proxy/https.go @@ -22,14 +22,14 @@ import ( "github.com/fatedier/frp/utils/vhost" ) -type HttpsProxy struct { +type HTTPSProxy struct { *BaseProxy - cfg *config.HttpsProxyConf + cfg *config.HTTPSProxyConf } -func (pxy *HttpsProxy) Run() (remoteAddr string, err error) { +func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) { xl := pxy.xl - routeConfig := &vhost.VhostRouteConfig{} + routeConfig := &vhost.RouteConfig{} defer func() { if err != nil { @@ -43,37 +43,37 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) { } routeConfig.Domain = domain - l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig) + l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig) if errRet != nil { err = errRet return } xl.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.listeners = append(pxy.listeners, l) - addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHttpsPort)) + addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort)) } if pxy.cfg.SubDomain != "" { routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost - l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig) + l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig) if errRet != nil { err = errRet return } xl.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.listeners = append(pxy.listeners, l) - addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpsPort))) + addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHTTPSPort))) } - pxy.startListenHandler(pxy, HandleUserTcpConnection) + pxy.startListenHandler(pxy, HandleUserTCPConnection) remoteAddr = strings.Join(addrs, ",") return } -func (pxy *HttpsProxy) GetConf() config.ProxyConf { +func (pxy *HTTPSProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *HttpsProxy) Close() { +func (pxy *HTTPSProxy) Close() { pxy.BaseProxy.Close() } diff --git a/server/proxy/proxy.go b/server/proxy/proxy.go index d3668a17..b82fce9e 100644 --- a/server/proxy/proxy.go +++ b/server/proxy/proxy.go @@ -102,7 +102,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, } xl.Info("get a new work connection: [%s]", workConn.RemoteAddr().String()) xl.Spawn().AppendPrefix(pxy.GetName()) - workConn = frpNet.NewContextConn(workConn, pxy.ctx) + workConn = frpNet.NewContextConn(pxy.ctx, workConn) var ( srcAddr string @@ -182,45 +182,45 @@ func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.Reso userInfo: userInfo, } switch cfg := pxyConf.(type) { - case *config.TcpProxyConf: + case *config.TCPProxyConf: basePxy.usedPortsNum = 1 - pxy = &TcpProxy{ + pxy = &TCPProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.TcpMuxProxyConf: - pxy = &TcpMuxProxy{ + case *config.TCPMuxProxyConf: + pxy = &TCPMuxProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.HttpProxyConf: - pxy = &HttpProxy{ + case *config.HTTPProxyConf: + pxy = &HTTPProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.HttpsProxyConf: - pxy = &HttpsProxy{ + case *config.HTTPSProxyConf: + pxy = &HTTPSProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.UdpProxyConf: + case *config.UDPProxyConf: basePxy.usedPortsNum = 1 - pxy = &UdpProxy{ + pxy = &UDPProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.StcpProxyConf: - pxy = &StcpProxy{ + case *config.STCPProxyConf: + pxy = &STCPProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.XtcpProxyConf: - pxy = &XtcpProxy{ + case *config.XTCPProxyConf: + pxy = &XTCPProxy{ BaseProxy: &basePxy, cfg: cfg, } - case *config.SudpProxyConf: - pxy = &SudpProxy{ + case *config.SUDPProxyConf: + pxy = &SUDPProxy{ BaseProxy: &basePxy, cfg: cfg, } @@ -230,9 +230,9 @@ func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.Reso return } -// HandleUserTcpConnection is used for incoming tcp user connections. +// HandleUserTCPConnection is used for incoming user TCP connections. // It can be used for tcp, http, https type. -func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, serverCfg config.ServerCommonConf) { +func HandleUserTCPConnection(pxy Proxy, userConn net.Conn, serverCfg config.ServerCommonConf) { xl := xlog.FromContextSafe(pxy.Context()) defer userConn.Close() @@ -283,20 +283,20 @@ func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, serverCfg config.Serv xl.Debug("join connections closed") } -type ProxyManager struct { +type Manager struct { // proxies indexed by proxy name pxys map[string]Proxy mu sync.RWMutex } -func NewProxyManager() *ProxyManager { - return &ProxyManager{ +func NewManager() *Manager { + return &Manager{ pxys: make(map[string]Proxy), } } -func (pm *ProxyManager) Add(name string, pxy Proxy) error { +func (pm *Manager) Add(name string, pxy Proxy) error { pm.mu.Lock() defer pm.mu.Unlock() if _, ok := pm.pxys[name]; ok { @@ -307,13 +307,13 @@ func (pm *ProxyManager) Add(name string, pxy Proxy) error { return nil } -func (pm *ProxyManager) Del(name string) { +func (pm *Manager) Del(name string) { pm.mu.Lock() defer pm.mu.Unlock() delete(pm.pxys, name) } -func (pm *ProxyManager) GetByName(name string) (pxy Proxy, ok bool) { +func (pm *Manager) GetByName(name string) (pxy Proxy, ok bool) { pm.mu.RLock() defer pm.mu.RUnlock() pxy, ok = pm.pxys[name] diff --git a/server/proxy/stcp.go b/server/proxy/stcp.go index 27bfb143..44be1b39 100644 --- a/server/proxy/stcp.go +++ b/server/proxy/stcp.go @@ -18,12 +18,12 @@ import ( "github.com/fatedier/frp/models/config" ) -type StcpProxy struct { +type STCPProxy struct { *BaseProxy - cfg *config.StcpProxyConf + cfg *config.STCPProxyConf } -func (pxy *StcpProxy) Run() (remoteAddr string, err error) { +func (pxy *STCPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk) if errRet != nil { @@ -33,15 +33,15 @@ func (pxy *StcpProxy) Run() (remoteAddr string, err error) { pxy.listeners = append(pxy.listeners, listener) xl.Info("stcp proxy custom listen success") - pxy.startListenHandler(pxy, HandleUserTcpConnection) + pxy.startListenHandler(pxy, HandleUserTCPConnection) return } -func (pxy *StcpProxy) GetConf() config.ProxyConf { +func (pxy *STCPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *StcpProxy) Close() { +func (pxy *STCPProxy) Close() { pxy.BaseProxy.Close() pxy.rc.VisitorManager.CloseListener(pxy.GetName()) } diff --git a/server/proxy/sudp.go b/server/proxy/sudp.go index 2916334a..182c792a 100644 --- a/server/proxy/sudp.go +++ b/server/proxy/sudp.go @@ -18,12 +18,12 @@ import ( "github.com/fatedier/frp/models/config" ) -type SudpProxy struct { +type SUDPProxy struct { *BaseProxy - cfg *config.SudpProxyConf + cfg *config.SUDPProxyConf } -func (pxy *SudpProxy) Run() (remoteAddr string, err error) { +func (pxy *SUDPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk) @@ -34,15 +34,15 @@ func (pxy *SudpProxy) Run() (remoteAddr string, err error) { pxy.listeners = append(pxy.listeners, listener) xl.Info("sudp proxy custom listen success") - pxy.startListenHandler(pxy, HandleUserTcpConnection) + pxy.startListenHandler(pxy, HandleUserTCPConnection) return } -func (pxy *SudpProxy) GetConf() config.ProxyConf { +func (pxy *SUDPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *SudpProxy) Close() { +func (pxy *SUDPProxy) Close() { pxy.BaseProxy.Close() pxy.rc.VisitorManager.CloseListener(pxy.GetName()) } diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go index 0ecfe260..786c1a5d 100644 --- a/server/proxy/tcp.go +++ b/server/proxy/tcp.go @@ -21,17 +21,17 @@ import ( "github.com/fatedier/frp/models/config" ) -type TcpProxy struct { +type TCPProxy struct { *BaseProxy - cfg *config.TcpProxyConf + cfg *config.TCPProxyConf realPort int } -func (pxy *TcpProxy) Run() (remoteAddr string, err error) { +func (pxy *TCPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl if pxy.cfg.Group != "" { - l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort) + l, realPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort) if errRet != nil { err = errRet return @@ -45,13 +45,13 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) { pxy.listeners = append(pxy.listeners, l) xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group) } else { - pxy.realPort, err = pxy.rc.TcpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort) + pxy.realPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort) if err != nil { return } defer func() { if err != nil { - pxy.rc.TcpPortManager.Release(pxy.realPort) + pxy.rc.TCPPortManager.Release(pxy.realPort) } }() listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort)) @@ -65,17 +65,17 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) { pxy.cfg.RemotePort = pxy.realPort remoteAddr = fmt.Sprintf(":%d", pxy.realPort) - pxy.startListenHandler(pxy, HandleUserTcpConnection) + pxy.startListenHandler(pxy, HandleUserTCPConnection) return } -func (pxy *TcpProxy) GetConf() config.ProxyConf { +func (pxy *TCPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *TcpProxy) Close() { +func (pxy *TCPProxy) Close() { pxy.BaseProxy.Close() if pxy.cfg.Group == "" { - pxy.rc.TcpPortManager.Release(pxy.realPort) + pxy.rc.TCPPortManager.Release(pxy.realPort) } } diff --git a/server/proxy/tcpmux.go b/server/proxy/tcpmux.go index 6af61c8e..d0da63be 100644 --- a/server/proxy/tcpmux.go +++ b/server/proxy/tcpmux.go @@ -25,30 +25,30 @@ import ( "github.com/fatedier/frp/utils/vhost" ) -type TcpMuxProxy struct { +type TCPMuxProxy struct { *BaseProxy - cfg *config.TcpMuxProxyConf + cfg *config.TCPMuxProxyConf } -func (pxy *TcpMuxProxy) httpConnectListen(domain string, addrs []string) (_ []string, err error) { +func (pxy *TCPMuxProxy) httpConnectListen(domain string, addrs []string) (_ []string, err error) { var l net.Listener if pxy.cfg.Group != "" { - l, err = pxy.rc.TcpMuxGroupCtl.Listen(pxy.cfg.Multiplexer, pxy.cfg.Group, pxy.cfg.GroupKey, domain, pxy.ctx) + l, err = pxy.rc.TCPMuxGroupCtl.Listen(pxy.ctx, pxy.cfg.Multiplexer, pxy.cfg.Group, pxy.cfg.GroupKey, domain) } else { - routeConfig := &vhost.VhostRouteConfig{ + routeConfig := &vhost.RouteConfig{ Domain: domain, } - l, err = pxy.rc.TcpMuxHttpConnectMuxer.Listen(pxy.ctx, routeConfig) + l, err = pxy.rc.TCPMuxHTTPConnectMuxer.Listen(pxy.ctx, routeConfig) } if err != nil { return nil, err } pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s]", domain) pxy.listeners = append(pxy.listeners, l) - return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TcpMuxHttpConnectPort)), nil + return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TCPMuxHTTPConnectPort)), nil } -func (pxy *TcpMuxProxy) httpConnectRun() (remoteAddr string, err error) { +func (pxy *TCPMuxProxy) httpConnectRun() (remoteAddr string, err error) { addrs := make([]string, 0) for _, domain := range pxy.cfg.CustomDomains { if domain == "" { @@ -68,14 +68,14 @@ func (pxy *TcpMuxProxy) httpConnectRun() (remoteAddr string, err error) { } } - pxy.startListenHandler(pxy, HandleUserTcpConnection) + pxy.startListenHandler(pxy, HandleUserTCPConnection) remoteAddr = strings.Join(addrs, ",") return remoteAddr, err } -func (pxy *TcpMuxProxy) Run() (remoteAddr string, err error) { +func (pxy *TCPMuxProxy) Run() (remoteAddr string, err error) { switch pxy.cfg.Multiplexer { - case consts.HttpConnectTcpMultiplexer: + case consts.HTTPConnectTCPMultiplexer: remoteAddr, err = pxy.httpConnectRun() default: err = fmt.Errorf("unknown multiplexer [%s]", pxy.cfg.Multiplexer) @@ -87,10 +87,10 @@ func (pxy *TcpMuxProxy) Run() (remoteAddr string, err error) { return remoteAddr, err } -func (pxy *TcpMuxProxy) GetConf() config.ProxyConf { +func (pxy *TCPMuxProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *TcpMuxProxy) Close() { +func (pxy *TCPMuxProxy) Close() { pxy.BaseProxy.Close() } diff --git a/server/proxy/udp.go b/server/proxy/udp.go index a0471839..d87e0efd 100644 --- a/server/proxy/udp.go +++ b/server/proxy/udp.go @@ -17,6 +17,7 @@ package proxy import ( "context" "fmt" + "io" "net" "time" @@ -24,13 +25,15 @@ import ( "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/proto/udp" "github.com/fatedier/frp/server/metrics" + frpNet "github.com/fatedier/frp/utils/net" "github.com/fatedier/golib/errors" + frpIo "github.com/fatedier/golib/io" ) -type UdpProxy struct { +type UDPProxy struct { *BaseProxy - cfg *config.UdpProxyConf + cfg *config.UDPProxyConf realPort int @@ -42,10 +45,10 @@ type UdpProxy struct { workConn net.Conn // sendCh is used for sending packages to workConn - sendCh chan *msg.UdpPacket + sendCh chan *msg.UDPPacket // readCh is used for reading packages from workConn - readCh chan *msg.UdpPacket + readCh chan *msg.UDPPacket // checkCloseCh is used for watching if workConn is closed checkCloseCh chan int @@ -53,15 +56,15 @@ type UdpProxy struct { isClosed bool } -func (pxy *UdpProxy) Run() (remoteAddr string, err error) { +func (pxy *UDPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl - pxy.realPort, err = pxy.rc.UdpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort) + pxy.realPort, err = pxy.rc.UDPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort) if err != nil { return } defer func() { if err != nil { - pxy.rc.UdpPortManager.Release(pxy.realPort) + pxy.rc.UDPPortManager.Release(pxy.realPort) } }() @@ -81,8 +84,8 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { xl.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort) pxy.udpConn = udpConn - pxy.sendCh = make(chan *msg.UdpPacket, 1024) - pxy.readCh = make(chan *msg.UdpPacket, 1024) + pxy.sendCh = make(chan *msg.UDPPacket, 1024) + pxy.readCh = make(chan *msg.UDPPacket, 1024) pxy.checkCloseCh = make(chan int) // read message from workConn, if it returns any error, notify proxy to start a new workConn @@ -110,7 +113,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { case *msg.Ping: xl.Trace("udp work conn get ping message") continue - case *msg.UdpPacket: + case *msg.UDPPacket: if errRet := errors.PanicToError(func() { xl.Trace("get udp message from workConn: %s", m.Content) pxy.readCh <- m @@ -142,15 +145,14 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { xl.Info("sender goroutine for udp work connection closed: %v", errRet) conn.Close() return - } else { - xl.Trace("send message to udp workConn: %s", udpMsg.Content) - metrics.Server.AddTrafficIn( - pxy.GetName(), - pxy.GetConf().GetBaseInfo().ProxyType, - int64(len(udpMsg.Content)), - ) - continue } + xl.Trace("send message to udp workConn: %s", udpMsg.Content) + metrics.Server.AddTrafficIn( + pxy.GetName(), + pxy.GetConf().GetBaseInfo().ProxyType, + int64(len(udpMsg.Content)), + ) + continue case <-ctx.Done(): xl.Info("sender goroutine for udp work connection closed") return @@ -175,14 +177,28 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { } continue } - // close the old workConn and replac it with a new one + // close the old workConn and replace it with a new one if pxy.workConn != nil { pxy.workConn.Close() } - pxy.workConn = workConn + + var rwc io.ReadWriteCloser = workConn + if pxy.cfg.UseEncryption { + rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token)) + if err != nil { + xl.Error("create encryption stream error: %v", err) + workConn.Close() + continue + } + } + if pxy.cfg.UseCompression { + rwc = frpIo.WithCompression(rwc) + } + + pxy.workConn = frpNet.WrapReadWriteCloserToConn(rwc, workConn) ctx, cancel := context.WithCancel(context.Background()) - go workConnReaderFn(workConn) - go workConnSenderFn(workConn, ctx) + go workConnReaderFn(pxy.workConn) + go workConnSenderFn(pxy.workConn, ctx) _, ok := <-pxy.checkCloseCh cancel() if !ok { @@ -196,17 +212,17 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) { // Response will be wrapped to be forwarded by work connection to server. // Close readCh and sendCh at the end. go func() { - udp.ForwardUserConn(udpConn, pxy.readCh, pxy.sendCh) + udp.ForwardUserConn(udpConn, pxy.readCh, pxy.sendCh, int(pxy.serverCfg.UDPPacketSize)) pxy.Close() }() return remoteAddr, nil } -func (pxy *UdpProxy) GetConf() config.ProxyConf { +func (pxy *UDPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *UdpProxy) Close() { +func (pxy *UDPProxy) Close() { pxy.mu.Lock() defer pxy.mu.Unlock() if !pxy.isClosed { @@ -223,5 +239,5 @@ func (pxy *UdpProxy) Close() { close(pxy.readCh) close(pxy.sendCh) } - pxy.rc.UdpPortManager.Release(pxy.realPort) + pxy.rc.UDPPortManager.Release(pxy.realPort) } diff --git a/server/proxy/xtcp.go b/server/proxy/xtcp.go index 92291c06..8dbb881a 100644 --- a/server/proxy/xtcp.go +++ b/server/proxy/xtcp.go @@ -23,14 +23,14 @@ import ( "github.com/fatedier/golib/errors" ) -type XtcpProxy struct { +type XTCPProxy struct { *BaseProxy - cfg *config.XtcpProxyConf + cfg *config.XTCPProxyConf closeCh chan struct{} } -func (pxy *XtcpProxy) Run() (remoteAddr string, err error) { +func (pxy *XTCPProxy) Run() (remoteAddr string, err error) { xl := pxy.xl if pxy.rc.NatHoleController == nil { @@ -84,11 +84,11 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) { return } -func (pxy *XtcpProxy) GetConf() config.ProxyConf { +func (pxy *XTCPProxy) GetConf() config.ProxyConf { return pxy.cfg } -func (pxy *XtcpProxy) Close() { +func (pxy *XTCPProxy) Close() { pxy.BaseProxy.Close() pxy.rc.NatHoleController.CloseClient(pxy.GetName()) errors.PanicToError(func() { diff --git a/server/service.go b/server/service.go index 4f1c702f..3880d382 100644 --- a/server/service.go +++ b/server/service.go @@ -17,16 +17,12 @@ package server import ( "bytes" "context" - "crypto/rand" - "crypto/rsa" "crypto/tls" - "crypto/x509" - "encoding/pem" "fmt" "io/ioutil" - "math/big" "net" "net/http" + "sort" "time" "github.com/fatedier/frp/assets" @@ -36,11 +32,13 @@ import ( "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/nathole" plugin "github.com/fatedier/frp/models/plugin/server" + "github.com/fatedier/frp/models/transport" "github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/group" "github.com/fatedier/frp/server/metrics" "github.com/fatedier/frp/server/ports" "github.com/fatedier/frp/server/proxy" + "github.com/fatedier/frp/server/visitor" "github.com/fatedier/frp/utils/log" frpNet "github.com/fatedier/frp/utils/net" "github.com/fatedier/frp/utils/tcpmux" @@ -79,13 +77,13 @@ type Service struct { ctlManager *ControlManager // Manage all proxies - pxyManager *proxy.ProxyManager + pxyManager *proxy.Manager // Manage all plugins pluginManager *plugin.Manager // HTTP vhost router - httpVhostRouter *vhost.VhostRouters + httpVhostRouter *vhost.Routers // All resource managers and controllers rc *controller.ResourceController @@ -99,53 +97,67 @@ type Service struct { } func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { + tlsConfig, err := transport.NewServerTLSConfig( + cfg.TLSCertFile, + cfg.TLSKeyFile, + cfg.TLSTrustedCaFile) + if err != nil { + return + } + svr = &Service{ ctlManager: NewControlManager(), - pxyManager: proxy.NewProxyManager(), + pxyManager: proxy.NewManager(), pluginManager: plugin.NewManager(), rc: &controller.ResourceController{ - VisitorManager: controller.NewVisitorManager(), - TcpPortManager: ports.NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts), - UdpPortManager: ports.NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts), + VisitorManager: visitor.NewManager(), + TCPPortManager: ports.NewManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts), + UDPPortManager: ports.NewManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts), }, - httpVhostRouter: vhost.NewVhostRouters(), - authVerifier: auth.NewAuthVerifier(cfg.AuthServerConfig), - tlsConfig: generateTLSConfig(), + httpVhostRouter: vhost.NewRouters(), + authVerifier: auth.NewAuthVerifier(cfg.ServerConfig), + tlsConfig: tlsConfig, cfg: cfg, } // Create tcpmux httpconnect multiplexer. - if cfg.TcpMuxHttpConnectPort > 0 { + if cfg.TCPMuxHTTPConnectPort > 0 { var l net.Listener - l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort)) + l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort)) if err != nil { err = fmt.Errorf("Create server listener error, %v", err) return } - svr.rc.TcpMuxHttpConnectMuxer, err = tcpmux.NewHttpConnectTcpMuxer(l, vhostReadWriteTimeout) + svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, vhostReadWriteTimeout) if err != nil { err = fmt.Errorf("Create vhost tcpMuxer error, %v", err) return } - log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort) + log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort) } // Init all plugins - for name, options := range cfg.HTTPPlugins { - svr.pluginManager.Register(plugin.NewHTTPPluginOptions(options)) + plugin_names := make([]string, 0, len(cfg.HTTPPlugins)) + for n := range cfg.HTTPPlugins { + plugin_names = append(plugin_names, n) + } + sort.Strings(plugin_names) + + for _, name := range plugin_names { + svr.pluginManager.Register(plugin.NewHTTPPluginOptions(cfg.HTTPPlugins[name])) log.Info("plugin [%s] has been registered", name) } svr.rc.PluginManager = svr.pluginManager // Init group controller - svr.rc.TcpGroupCtl = group.NewTcpGroupCtl(svr.rc.TcpPortManager) + svr.rc.TCPGroupCtl = group.NewTCPGroupCtl(svr.rc.TCPPortManager) // Init HTTP group controller svr.rc.HTTPGroupCtl = group.NewHTTPGroupController(svr.httpVhostRouter) // Init TCP mux group controller - svr.rc.TcpMuxGroupCtl = group.NewTcpMuxGroupCtl(svr.rc.TcpMuxHttpConnectMuxer) + svr.rc.TCPMuxGroupCtl = group.NewTCPMuxGroupCtl(svr.rc.TCPMuxHTTPConnectMuxer) // Init 404 not found page vhost.NotFoundPagePath = cfg.Custom404Page @@ -155,10 +167,10 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { httpsMuxOn bool ) if cfg.BindAddr == cfg.ProxyBindAddr { - if cfg.BindPort == cfg.VhostHttpPort { + if cfg.BindPort == cfg.VhostHTTPPort { httpMuxOn = true } - if cfg.BindPort == cfg.VhostHttpsPort { + if cfg.BindPort == cfg.VhostHTTPSPort { httpsMuxOn = true } } @@ -178,13 +190,13 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { log.Info("frps tcp listen on %s:%d", cfg.BindAddr, cfg.BindPort) // Listen for accepting connections from client using kcp protocol. - if cfg.KcpBindPort > 0 { - svr.kcpListener, err = frpNet.ListenKcp(cfg.BindAddr, cfg.KcpBindPort) + if cfg.KCPBindPort > 0 { + svr.kcpListener, err = frpNet.ListenKcp(cfg.BindAddr, cfg.KCPBindPort) if err != nil { - err = fmt.Errorf("Listen on kcp address udp [%s:%d] error: %v", cfg.BindAddr, cfg.KcpBindPort, err) + err = fmt.Errorf("Listen on kcp address udp [%s:%d] error: %v", cfg.BindAddr, cfg.KCPBindPort, err) return } - log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KcpBindPort) + log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KCPBindPort) } // Listen for accepting connections from client using websocket protocol. @@ -195,13 +207,13 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { svr.websocketListener = frpNet.NewWebsocketListener(websocketLn) // Create http vhost muxer. - if cfg.VhostHttpPort > 0 { - rp := vhost.NewHttpReverseProxy(vhost.HttpReverseProxyOptions{ - ResponseHeaderTimeoutS: cfg.VhostHttpTimeout, + if cfg.VhostHTTPPort > 0 { + rp := vhost.NewHTTPReverseProxy(vhost.HTTPReverseProxyOptions{ + ResponseHeaderTimeoutS: cfg.VhostHTTPTimeout, }, svr.httpVhostRouter) - svr.rc.HttpReverseProxy = rp + svr.rc.HTTPReverseProxy = rp - address := fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.VhostHttpPort) + address := fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPPort) server := &http.Server{ Addr: address, Handler: rp, @@ -217,46 +229,46 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { } } go server.Serve(l) - log.Info("http service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpPort) + log.Info("http service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPPort) } // Create https vhost muxer. - if cfg.VhostHttpsPort > 0 { + if cfg.VhostHTTPSPort > 0 { var l net.Listener if httpsMuxOn { l = svr.muxer.ListenHttps(1) } else { - l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort)) + l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPSPort)) if err != nil { err = fmt.Errorf("Create server listener error, %v", err) return } } - svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, vhostReadWriteTimeout) + svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout) if err != nil { err = fmt.Errorf("Create vhost httpsMuxer error, %v", err) return } - log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort) + log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPSPort) } // frp tls listener svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool { - return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE + return int(data[0]) == frpNet.FRPTLSHeadByte }) // Create nat hole controller. - if cfg.BindUdpPort > 0 { - var nc *nathole.NatHoleController - addr := fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.BindUdpPort) - nc, err = nathole.NewNatHoleController(addr) + if cfg.BindUDPPort > 0 { + var nc *nathole.Controller + addr := fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.BindUDPPort) + nc, err = nathole.NewController(addr) if err != nil { err = fmt.Errorf("Create nat hole controller error, %v", err) return } svr.rc.NatHoleController = nc - log.Info("nat hole udp service listen on %s:%d", cfg.BindAddr, cfg.BindUdpPort) + log.Info("nat hole udp service listen on %s:%d", cfg.BindAddr, cfg.BindUDPPort) } var statsEnable bool @@ -290,7 +302,7 @@ func (svr *Service) Run() { if svr.rc.NatHoleController != nil { go svr.rc.NatHoleController.Run() } - if svr.cfg.KcpBindPort > 0 { + if svr.cfg.KCPBindPort > 0 { go svr.HandleListener(svr.kcpListener) } @@ -374,11 +386,11 @@ func (svr *Service) HandleListener(l net.Listener) { xl := xlog.New() ctx := context.Background() - c = frpNet.NewContextConn(c, xlog.NewContext(ctx, xl)) + c = frpNet.NewContextConn(xlog.NewContext(ctx, xl), c) log.Trace("start check TLS connection...") originConn := c - c, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TlsOnly, connReadTimeout) + c, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TLSOnly, connReadTimeout) if err != nil { log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err) originConn.Close() @@ -388,7 +400,7 @@ func (svr *Service) HandleListener(l net.Listener) { // Start a new goroutine for dealing connections. go func(ctx context.Context, frpConn net.Conn) { - if svr.cfg.TcpMux { + if svr.cfg.TCPMux { fmuxCfg := fmux.DefaultConfig() fmuxCfg.KeepAliveInterval = 20 * time.Second fmuxCfg.LogOutput = ioutil.Discard @@ -416,10 +428,10 @@ func (svr *Service) HandleListener(l net.Listener) { } func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err error) { - // If client's RunId is empty, it's a new client, we just create a new controller. + // If client's RunID is empty, it's a new client, we just create a new controller. // Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one. - if loginMsg.RunId == "" { - loginMsg.RunId, err = util.RandId() + if loginMsg.RunID == "" { + loginMsg.RunID, err = util.RandID() if err != nil { return } @@ -427,7 +439,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err ctx := frpNet.NewContextFromConn(ctlConn) xl := xlog.FromContextSafe(ctx) - xl.AppendPrefix(loginMsg.RunId) + xl.AppendPrefix(loginMsg.RunID) ctx = xlog.NewContext(ctx, xl) xl.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]", ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch) @@ -444,7 +456,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err } ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, svr.authVerifier, ctlConn, loginMsg, svr.cfg) - if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil { + if oldCtl := svr.ctlManager.Add(loginMsg.RunID, ctl); oldCtl != nil { oldCtl.allShutdown.WaitDone() } @@ -456,7 +468,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err go func() { // block until control closed ctl.WaitClosed() - svr.ctlManager.Del(loginMsg.RunId, ctl) + svr.ctlManager.Del(loginMsg.RunID, ctl) }() return } @@ -464,17 +476,17 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err // RegisterWorkConn register a new work connection to control and proxies need it. func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) error { xl := frpNet.NewLogFromConn(workConn) - ctl, exist := svr.ctlManager.GetById(newMsg.RunId) + ctl, exist := svr.ctlManager.GetByID(newMsg.RunID) if !exist { - xl.Warn("No client control found for run id [%s]", newMsg.RunId) - return fmt.Errorf("no client control found for run id [%s]", newMsg.RunId) + xl.Warn("No client control found for run id [%s]", newMsg.RunID) + return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID) } // server plugin hook content := &plugin.NewWorkConnContent{ User: plugin.UserInfo{ User: ctl.loginMsg.User, Metas: ctl.loginMsg.Metas, - RunId: ctl.loginMsg.RunId, + RunID: ctl.loginMsg.RunID, }, NewWorkConn: *newMsg, } @@ -485,11 +497,11 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) err = svr.authVerifier.VerifyNewWorkConn(newMsg) } if err != nil { - xl.Warn("invalid NewWorkConn with run id [%s]", newMsg.RunId) + xl.Warn("invalid NewWorkConn with run id [%s]", newMsg.RunID) msg.WriteMsg(workConn, &msg.StartWorkConn{ Error: util.GenerateResponseErrorString("invalid NewWorkConn", err, ctl.serverCfg.DetailedErrorsToClient), }) - return fmt.Errorf("invalid NewWorkConn with run id [%s]", newMsg.RunId) + return fmt.Errorf("invalid NewWorkConn with run id [%s]", newMsg.RunID) } return ctl.RegisterWorkConn(workConn) } @@ -498,24 +510,3 @@ func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVis 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}} -} diff --git a/server/controller/visitor.go b/server/visitor/visitor.go similarity index 85% rename from server/controller/visitor.go rename to server/visitor/visitor.go index 22ff6942..7b54ab13 100644 --- a/server/controller/visitor.go +++ b/server/visitor/visitor.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package controller +package visitor import ( "fmt" @@ -27,21 +27,21 @@ import ( ) // Manager for visitor listeners. -type VisitorManager struct { +type Manager struct { visitorListeners map[string]*frpNet.CustomListener skMap map[string]string mu sync.RWMutex } -func NewVisitorManager() *VisitorManager { - return &VisitorManager{ +func NewManager() *Manager { + return &Manager{ visitorListeners: make(map[string]*frpNet.CustomListener), skMap: make(map[string]string), } } -func (vm *VisitorManager) Listen(name string, sk string) (l *frpNet.CustomListener, err error) { +func (vm *Manager) Listen(name string, sk string) (l *frpNet.CustomListener, err error) { vm.mu.Lock() defer vm.mu.Unlock() @@ -56,7 +56,7 @@ func (vm *VisitorManager) Listen(name string, sk string) (l *frpNet.CustomListen return } -func (vm *VisitorManager) NewConn(name string, conn net.Conn, timestamp int64, signKey string, +func (vm *Manager) NewConn(name string, conn net.Conn, timestamp int64, signKey string, useEncryption bool, useCompression bool) (err error) { vm.mu.RLock() @@ -87,7 +87,7 @@ func (vm *VisitorManager) NewConn(name string, conn net.Conn, timestamp int64, s return } -func (vm *VisitorManager) CloseListener(name string) { +func (vm *Manager) CloseListener(name string) { vm.mu.Lock() defer vm.mu.Unlock() diff --git a/test/e2e/basic/basic.go b/test/e2e/basic/basic.go new file mode 100644 index 00000000..ada4df68 --- /dev/null +++ b/test/e2e/basic/basic.go @@ -0,0 +1,198 @@ +package basic + +import ( + "fmt" + "strings" + "time" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/test/e2e/framework/consts" + + . "github.com/onsi/ginkgo" +) + +var connTimeout = 2 * time.Second + +var _ = Describe("[Feature: Basic]", func() { + f := framework.NewDefaultFramework() + + Describe("TCP && UDP", func() { + types := []string{"tcp", "udp"} + for _, t := range types { + proxyType := t + It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() { + serverConf := consts.DefaultServerConfig + clientConf := consts.DefaultClientConfig + + localPortName := "" + protocol := "tcp" + switch proxyType { + case "tcp": + localPortName = framework.TCPEchoServerPort + protocol = "tcp" + case "udp": + localPortName = framework.UDPEchoServerPort + protocol = "udp" + } + getProxyConf := func(proxyName string, portName string, extra string) string { + return fmt.Sprintf(` + [%s] + type = %s + local_port = {{ .%s }} + remote_port = {{ .%s }} + `+extra, proxyName, proxyType, localPortName, portName) + } + + tests := []struct { + proxyName string + portName string + extraConfig string + }{ + { + proxyName: "normal", + portName: framework.GenPortName("Normal"), + }, + { + proxyName: "with-encryption", + portName: framework.GenPortName("WithEncryption"), + extraConfig: "use_encryption = true", + }, + { + proxyName: "with-compression", + portName: framework.GenPortName("WithCompression"), + extraConfig: "use_compression = true", + }, + { + proxyName: "with-encryption-and-compression", + portName: framework.GenPortName("WithEncryptionAndCompression"), + extraConfig: ` + use_encryption = true + use_compression = true + `, + }, + } + + // build all client config + for _, test := range tests { + clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n" + } + // run frps and frpc + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + for _, test := range tests { + framework.ExpectRequest(protocol, f.UsedPorts[test.portName], + []byte(consts.TestString), []byte(consts.TestString), connTimeout, test.proxyName) + } + }) + } + }) + + Describe("STCP && SUDP", func() { + types := []string{"stcp", "sudp"} + for _, t := range types { + proxyType := t + It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() { + serverConf := consts.DefaultServerConfig + clientServerConf := consts.DefaultClientConfig + clientVisitorConf := consts.DefaultClientConfig + + localPortName := "" + protocol := "tcp" + switch proxyType { + case "stcp": + localPortName = framework.TCPEchoServerPort + protocol = "tcp" + case "sudp": + localPortName = framework.UDPEchoServerPort + protocol = "udp" + } + + correctSK := "abc" + wrongSK := "123" + + getProxyServerConf := func(proxyName string, extra string) string { + return fmt.Sprintf(` + [%s] + type = %s + role = server + sk = %s + local_port = {{ .%s }} + `+extra, proxyName, proxyType, correctSK, localPortName) + } + getProxyVisitorConf := func(proxyName string, portName, visitorSK, extra string) string { + return fmt.Sprintf(` + [%s] + type = %s + role = visitor + server_name = %s + sk = %s + bind_port = {{ .%s }} + `+extra, proxyName, proxyType, proxyName, visitorSK, portName) + } + + tests := []struct { + proxyName string + bindPortName string + visitorSK string + extraConfig string + expectError bool + }{ + { + proxyName: "normal", + bindPortName: framework.GenPortName("Normal"), + visitorSK: correctSK, + }, + { + proxyName: "with-encryption", + bindPortName: framework.GenPortName("WithEncryption"), + visitorSK: correctSK, + extraConfig: "use_encryption = true", + }, + { + proxyName: "with-compression", + bindPortName: framework.GenPortName("WithCompression"), + visitorSK: correctSK, + extraConfig: "use_compression = true", + }, + { + proxyName: "with-encryption-and-compression", + bindPortName: framework.GenPortName("WithEncryptionAndCompression"), + visitorSK: correctSK, + extraConfig: ` + use_encryption = true + use_compression = true + `, + }, + { + proxyName: "with-error-sk", + bindPortName: framework.GenPortName("WithErrorSK"), + visitorSK: wrongSK, + expectError: true, + }, + } + + // build all client config + for _, test := range tests { + clientServerConf += getProxyServerConf(test.proxyName, test.extraConfig) + "\n" + } + for _, test := range tests { + clientVisitorConf += getProxyVisitorConf(test.proxyName, test.bindPortName, test.visitorSK, test.extraConfig) + "\n" + } + // run frps and frpc + f.RunProcesses([]string{serverConf}, []string{clientServerConf, clientVisitorConf}) + + for _, test := range tests { + expectResp := []byte(consts.TestString) + if test.expectError { + framework.ExpectRequestError(protocol, f.UsedPorts[test.bindPortName], + []byte(consts.TestString), connTimeout, test.proxyName) + continue + } + + framework.ExpectRequest(protocol, f.UsedPorts[test.bindPortName], + []byte(consts.TestString), expectResp, connTimeout, test.proxyName) + } + }) + } + }) +}) diff --git a/test/e2e/basic/client_server.go b/test/e2e/basic/client_server.go new file mode 100644 index 00000000..5fb755db --- /dev/null +++ b/test/e2e/basic/client_server.go @@ -0,0 +1,115 @@ +package basic + +import ( + "fmt" + "strings" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/test/e2e/framework/consts" + + . "github.com/onsi/ginkgo" +) + +type generalTestConfigures struct { + server string + client string + expectError bool +} + +func defineClientServerTest(desc string, f *framework.Framework, configures *generalTestConfigures) { + It(desc, func() { + serverConf := consts.DefaultServerConfig + clientConf := consts.DefaultClientConfig + + serverConf += fmt.Sprintf(` + %s + `, configures.server) + + clientConf += fmt.Sprintf(` + %s + + [tcp] + type = tcp + local_port = {{ .%s }} + remote_port = {{ .%s }} + + [udp] + type = udp + local_port = {{ .%s }} + remote_port = {{ .%s }} + `, configures.client, + framework.TCPEchoServerPort, framework.GenPortName("TCP"), + framework.UDPEchoServerPort, framework.GenPortName("UDP"), + ) + + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + if !configures.expectError { + framework.ExpectTCPRequest(f.UsedPorts[framework.GenPortName("TCP")], + []byte(consts.TestString), []byte(consts.TestString), connTimeout, "tcp proxy") + framework.ExpectUDPRequest(f.UsedPorts[framework.GenPortName("UDP")], + []byte(consts.TestString), []byte(consts.TestString), connTimeout, "udp proxy") + } else { + framework.ExpectTCPRequestError(f.UsedPorts[framework.GenPortName("TCP")], + []byte(consts.TestString), connTimeout, "tcp proxy") + framework.ExpectUDPRequestError(f.UsedPorts[framework.GenPortName("UDP")], + []byte(consts.TestString), connTimeout, "udp proxy") + } + }) +} + +var _ = Describe("[Feature: Client-Server]", func() { + f := framework.NewDefaultFramework() + + Describe("Protocol", func() { + supportProtocols := []string{"tcp", "kcp", "websocket"} + for _, protocol := range supportProtocols { + configures := &generalTestConfigures{ + server: fmt.Sprintf(` + kcp_bind_port = {{ .%s }} + protocol = %s" + `, consts.PortServerName, protocol), + client: "protocol = " + protocol, + } + defineClientServerTest(protocol, f, configures) + } + }) + + Describe("Authentication", func() { + defineClientServerTest("Token Correct", f, &generalTestConfigures{ + server: "token = 123456", + client: "token = 123456", + }) + + defineClientServerTest("Token Incorrect", f, &generalTestConfigures{ + server: "token = 123456", + client: "token = invalid", + expectError: true, + }) + }) + + Describe("TLS", func() { + supportProtocols := []string{"tcp", "kcp", "websocket"} + for _, protocol := range supportProtocols { + tmp := protocol + defineClientServerTest("TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{ + server: fmt.Sprintf(` + kcp_bind_port = {{ .%s }} + protocol = %s + `, consts.PortServerName, protocol), + client: fmt.Sprintf(`tls_enable = true + protocol = %s + `, protocol), + }) + } + + defineClientServerTest("enable tls_only, client with TLS", f, &generalTestConfigures{ + server: "tls_only = true", + client: "tls_enable = true", + }) + defineClientServerTest("enable tls_only, client without TLS", f, &generalTestConfigures{ + server: "tls_only = true", + expectError: true, + }) + }) +}) diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go new file mode 100644 index 00000000..60d73c5a --- /dev/null +++ b/test/e2e/e2e.go @@ -0,0 +1,62 @@ +package e2e + +import ( + "testing" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/utils/log" + + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/config" + "github.com/onsi/gomega" +) + +var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { + setupSuite() + return nil +}, func(data []byte) { + // Run on all Ginkgo nodes + setupSuitePerGinkgoNode() +}) + +var _ = ginkgo.SynchronizedAfterSuite(func() { + CleanupSuite() +}, func() { + AfterSuiteActions() +}) + +// RunE2ETests checks configuration parameters (specified through flags) and then runs +// E2E tests using the Ginkgo runner. +// If a "report directory" is specified, one or more JUnit test reports will be +// generated in this directory, and cluster logs will also be saved. +// This function is called on each Ginkgo node in parallel mode. +func RunE2ETests(t *testing.T) { + gomega.RegisterFailHandler(framework.Fail) + + log.Info("Starting e2e run %q on Ginkgo node %d of total %d", + framework.RunID, config.GinkgoConfig.ParallelNode, config.GinkgoConfig.ParallelTotal) + ginkgo.RunSpecs(t, "frp e2e suite") +} + +// setupSuite is the boilerplate that can be used to setup ginkgo test suites, on the SynchronizedBeforeSuite step. +// There are certain operations we only want to run once per overall test invocation +// (such as deleting old namespaces, or verifying that all system pods are running. +// Because of the way Ginkgo runs tests in parallel, we must use SynchronizedBeforeSuite +// to ensure that these operations only run on the first parallel Ginkgo node. +// +// This function takes two parameters: one function which runs on only the first Ginkgo node, +// returning an opaque byte array, and then a second function which runs on all Ginkgo nodes, +// accepting the byte array. +func setupSuite() { + // Run only on Ginkgo node 1 + // TODO +} + +// setupSuitePerGinkgoNode is the boilerplate that can be used to setup ginkgo test suites, on the SynchronizedBeforeSuite step. +// There are certain operations we only want to run once per overall test invocation on each Ginkgo node +// such as making some global variables accessible to all parallel executions +// Because of the way Ginkgo runs tests in parallel, we must use SynchronizedBeforeSuite +// Ref: https://onsi.github.io/ginkgo/#parallel-specs +func setupSuitePerGinkgoNode() { + // config.GinkgoConfig.ParallelNode +} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 00000000..afe77c3d --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,40 @@ +package e2e + +import ( + "flag" + "fmt" + "os" + "testing" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/utils/log" + + // test source + _ "github.com/fatedier/frp/test/e2e/basic" + _ "github.com/fatedier/frp/test/e2e/plugin" + + _ "github.com/onsi/ginkgo" +) + +// handleFlags sets up all flags and parses the command line. +func handleFlags() { + framework.RegisterCommonFlags(flag.CommandLine) + flag.Parse() +} + +func TestMain(m *testing.M) { + // Register test flags, then parse flags. + handleFlags() + + if err := framework.ValidateTestContext(&framework.TestContext); err != nil { + fmt.Println(err) + os.Exit(1) + } + + log.InitLog("console", "", framework.TestContext.LogLevel, 0, true) + os.Exit(m.Run()) +} + +func TestE2E(t *testing.T) { + RunE2ETests(t) +} diff --git a/test/e2e/examples.go b/test/e2e/examples.go new file mode 100644 index 00000000..0628bdcd --- /dev/null +++ b/test/e2e/examples.go @@ -0,0 +1,35 @@ +package e2e + +import ( + "fmt" + "time" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/test/e2e/framework/consts" + + . "github.com/onsi/ginkgo" +) + +var connTimeout = 2 * time.Second + +var _ = Describe("[Feature: Example]", func() { + f := framework.NewDefaultFramework() + + Describe("TCP", func() { + It("Expose a TCP echo server", func() { + serverConf := consts.DefaultServerConfig + clientConf := consts.DefaultClientConfig + + clientConf += fmt.Sprintf(` + [tcp] + type = tcp + local_port = {{ .%s }} + remote_port = {{ .%s }} + `, framework.TCPEchoServerPort, framework.GenPortName("TCP")) + + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + framework.ExpectTCPRequest(f.UsedPorts[framework.GenPortName("TCP")], []byte(consts.TestString), []byte(consts.TestString), connTimeout) + }) + }) +}) diff --git a/test/e2e/framework/cleanup.go b/test/e2e/framework/cleanup.go new file mode 100644 index 00000000..d2d1c5a6 --- /dev/null +++ b/test/e2e/framework/cleanup.go @@ -0,0 +1,59 @@ +package framework + +import ( + "sync" +) + +// CleanupActionHandle is an integer pointer type for handling cleanup action +type CleanupActionHandle *int +type cleanupFuncHandle struct { + actionHandle CleanupActionHandle + actionHook func() +} + +var cleanupActionsLock sync.Mutex +var cleanupHookList = []cleanupFuncHandle{} + +// AddCleanupAction installs a function that will be called in the event of the +// whole test being terminated. This allows arbitrary pieces of the overall +// test to hook into SynchronizedAfterSuite(). +// The hooks are called in last-in-first-out order. +func AddCleanupAction(fn func()) CleanupActionHandle { + p := CleanupActionHandle(new(int)) + cleanupActionsLock.Lock() + defer cleanupActionsLock.Unlock() + c := cleanupFuncHandle{actionHandle: p, actionHook: fn} + cleanupHookList = append([]cleanupFuncHandle{c}, cleanupHookList...) + return p +} + +// RemoveCleanupAction removes a function that was installed by +// AddCleanupAction. +func RemoveCleanupAction(p CleanupActionHandle) { + cleanupActionsLock.Lock() + defer cleanupActionsLock.Unlock() + for i, item := range cleanupHookList { + if item.actionHandle == p { + cleanupHookList = append(cleanupHookList[:i], cleanupHookList[i+1:]...) + break + } + } +} + +// RunCleanupActions runs all functions installed by AddCleanupAction. It does +// not remove them (see RemoveCleanupAction) but it does run unlocked, so they +// may remove themselves. +func RunCleanupActions() { + list := []func(){} + func() { + cleanupActionsLock.Lock() + defer cleanupActionsLock.Unlock() + for _, p := range cleanupHookList { + list = append(list, p.actionHook) + } + }() + // Run unlocked. + for _, fn := range list { + fn() + } +} diff --git a/test/e2e/framework/client.go b/test/e2e/framework/client.go new file mode 100644 index 00000000..76614354 --- /dev/null +++ b/test/e2e/framework/client.go @@ -0,0 +1,11 @@ +package framework + +type FRPClient struct { + port int +} + +func (f *Framework) FRPClient(port int) *FRPClient { + return &FRPClient{ + port: port, + } +} diff --git a/test/e2e/framework/consts/consts.go b/test/e2e/framework/consts/consts.go new file mode 100644 index 00000000..26bc92f0 --- /dev/null +++ b/test/e2e/framework/consts/consts.go @@ -0,0 +1,21 @@ +package consts + +const ( + TestString = "frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." +) + +const ( + PortServerName = "PortServer" +) + +const ( + DefaultServerConfig = ` + [common] + bind_port = {{ .PortServer }} + ` + + DefaultClientConfig = ` + [common] + server_port = {{ .PortServer }} + ` +) diff --git a/test/e2e/framework/expect.go b/test/e2e/framework/expect.go new file mode 100644 index 00000000..d2058e77 --- /dev/null +++ b/test/e2e/framework/expect.go @@ -0,0 +1,55 @@ +package framework + +import ( + "github.com/onsi/gomega" +) + +// ExpectEqual expects the specified two are the same, otherwise an exception raises +func ExpectEqual(actual interface{}, extra interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).To(gomega.Equal(extra), explain...) +} + +// ExpectEqualValues expects the specified two are the same, it not strict about type +func ExpectEqualValues(actual interface{}, extra interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).To(gomega.BeEquivalentTo(extra), explain...) +} + +// ExpectNotEqual expects the specified two are not the same, otherwise an exception raises +func ExpectNotEqual(actual interface{}, extra interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).NotTo(gomega.Equal(extra), explain...) +} + +// ExpectError expects an error happens, otherwise an exception raises +func ExpectError(err error, explain ...interface{}) { + gomega.ExpectWithOffset(1, err).To(gomega.HaveOccurred(), explain...) +} + +// ExpectNoError checks if "err" is set, and if so, fails assertion while logging the error. +func ExpectNoError(err error, explain ...interface{}) { + ExpectNoErrorWithOffset(1, err, explain...) +} + +// ExpectNoErrorWithOffset checks if "err" is set, and if so, fails assertion while logging the error at "offset" levels above its caller +// (for example, for call chain f -> g -> ExpectNoErrorWithOffset(1, ...) error would be logged for "f"). +func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) { + gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...) +} + +// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter. +func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...) +} + +// ExpectHaveKey expects the actual map has the key in the keyset +func ExpectHaveKey(actual interface{}, key interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).To(gomega.HaveKey(key), explain...) +} + +// ExpectEmpty expects actual is empty +func ExpectEmpty(actual interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).To(gomega.BeEmpty(), explain...) +} + +func ExpectTrue(actual interface{}, explain ...interface{}) { + gomega.ExpectWithOffset(1, actual).Should(gomega.BeTrue(), explain...) +} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go new file mode 100644 index 00000000..662f80d6 --- /dev/null +++ b/test/e2e/framework/framework.go @@ -0,0 +1,187 @@ +package framework + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + "text/template" + + "github.com/fatedier/frp/test/e2e/pkg/port" + "github.com/fatedier/frp/test/e2e/pkg/process" + + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/config" +) + +type Options struct { + TotalParallelNode int + CurrentNodeIndex int + FromPortIndex int + ToPortIndex int +} + +type Framework struct { + TempDirectory string + UsedPorts map[string]int + + portAllocator *port.Allocator + + // Multiple mock servers used for e2e testing. + mockServers *MockServers + + // To make sure that this framework cleans up after itself, no matter what, + // we install a Cleanup action before each test and clear it after. If we + // should abort, the AfterSuite hook should run all Cleanup actions. + cleanupHandle CleanupActionHandle + + // beforeEachStarted indicates that BeforeEach has started + beforeEachStarted bool + + serverConfPaths []string + serverProcesses []*process.Process + clientConfPaths []string + clientProcesses []*process.Process +} + +func NewDefaultFramework() *Framework { + options := Options{ + TotalParallelNode: config.GinkgoConfig.ParallelTotal, + CurrentNodeIndex: config.GinkgoConfig.ParallelNode, + FromPortIndex: 20000, + ToPortIndex: 50000, + } + return NewFramework(options) +} + +func NewFramework(opt Options) *Framework { + f := &Framework{ + portAllocator: port.NewAllocator(opt.FromPortIndex, opt.ToPortIndex, opt.TotalParallelNode, opt.CurrentNodeIndex-1), + } + + ginkgo.BeforeEach(f.BeforeEach) + ginkgo.AfterEach(f.AfterEach) + return f +} + +// BeforeEach create a temp directory. +func (f *Framework) BeforeEach() { + f.beforeEachStarted = true + + f.cleanupHandle = AddCleanupAction(f.AfterEach) + + dir, err := ioutil.TempDir(os.TempDir(), "frpe2e-test-*") + ExpectNoError(err) + f.TempDirectory = dir + + f.mockServers = NewMockServers(f.portAllocator) + if err := f.mockServers.Run(); err != nil { + Failf("%v", err) + } +} + +func (f *Framework) AfterEach() { + if !f.beforeEachStarted { + return + } + + RemoveCleanupAction(f.cleanupHandle) + + // stop processor + for _, p := range f.serverProcesses { + p.Stop() + if TestContext.Debug { + fmt.Println(p.ErrorOutput()) + fmt.Println(p.StdOutput()) + } + } + for _, p := range f.clientProcesses { + p.Stop() + if TestContext.Debug { + fmt.Println(p.ErrorOutput()) + fmt.Println(p.StdOutput()) + } + } + f.serverProcesses = nil + f.clientProcesses = nil + + // close mock servers + f.mockServers.Close() + + // clean directory + os.RemoveAll(f.TempDirectory) + f.TempDirectory = "" + f.serverConfPaths = nil + f.clientConfPaths = nil + + // release used ports + for _, port := range f.UsedPorts { + f.portAllocator.Release(port) + } + f.UsedPorts = nil +} + +var portRegex = regexp.MustCompile(`{{ \.Port.*? }}`) + +// RenderPortsTemplate render templates with ports. +// +// Local: {{ .Port1 }} +// Target: {{ .Port2 }} +// +// return rendered content and all allocated ports. +func (f *Framework) genPortsFromTemplates(templates []string) (ports map[string]int, err error) { + ports = make(map[string]int) + for _, t := range templates { + arrs := portRegex.FindAllString(t, -1) + for _, str := range arrs { + str = strings.TrimPrefix(str, "{{ .") + str = strings.TrimSuffix(str, " }}") + str = strings.TrimSpace(str) + ports[str] = 0 + } + } + defer func() { + if err != nil { + for _, port := range ports { + f.portAllocator.Release(port) + } + } + }() + + for name := range ports { + port := f.portAllocator.Get() + if port <= 0 { + return nil, fmt.Errorf("can't allocate port") + } + ports[name] = port + } + return + +} + +func (f *Framework) RenderTemplates(templates []string) (outs []string, ports map[string]int, err error) { + ports, err = f.genPortsFromTemplates(templates) + if err != nil { + return + } + + params := f.mockServers.GetTemplateParams() + for name, port := range ports { + params[name] = port + } + + for _, t := range templates { + tmpl, err := template.New("").Parse(t) + if err != nil { + return nil, nil, err + } + buffer := bytes.NewBuffer(nil) + if err = tmpl.Execute(buffer, params); err != nil { + return nil, nil, err + } + outs = append(outs, buffer.String()) + } + return +} diff --git a/test/e2e/framework/ginkgowrapper/wrapper.go b/test/e2e/framework/ginkgowrapper/wrapper.go new file mode 100644 index 00000000..01ccf4d9 --- /dev/null +++ b/test/e2e/framework/ginkgowrapper/wrapper.go @@ -0,0 +1,80 @@ +// Package ginkgowrapper wraps Ginkgo Fail and Skip functions to panic +// with structured data instead of a constant string. +package ginkgowrapper + +import ( + "bufio" + "bytes" + "regexp" + "runtime" + "runtime/debug" + "strings" + + "github.com/onsi/ginkgo" +) + +// FailurePanic is the value that will be panicked from Fail. +type FailurePanic struct { + Message string // The failure message passed to Fail + Filename string // The filename that is the source of the failure + Line int // The line number of the filename that is the source of the failure + FullStackTrace string // A full stack trace starting at the source of the failure +} + +// String makes FailurePanic look like the old Ginkgo panic when printed. +func (FailurePanic) String() string { return ginkgo.GINKGO_PANIC } + +// Fail wraps ginkgo.Fail so that it panics with more useful +// information about the failure. This function will panic with a +// FailurePanic. +func Fail(message string, callerSkip ...int) { + skip := 1 + if len(callerSkip) > 0 { + skip += callerSkip[0] + } + + _, file, line, _ := runtime.Caller(skip) + fp := FailurePanic{ + Message: message, + Filename: file, + Line: line, + FullStackTrace: pruneStack(skip), + } + + defer func() { + e := recover() + if e != nil { + panic(fp) + } + }() + + ginkgo.Fail(message, skip) +} + +// ginkgo adds a lot of test running infrastructure to the stack, so +// we filter those out +var stackSkipPattern = regexp.MustCompile(`onsi/ginkgo`) + +func pruneStack(skip int) string { + skip += 2 // one for pruneStack and one for debug.Stack + stack := debug.Stack() + scanner := bufio.NewScanner(bytes.NewBuffer(stack)) + var prunedStack []string + + // skip the top of the stack + for i := 0; i < 2*skip+1; i++ { + scanner.Scan() + } + + for scanner.Scan() { + if stackSkipPattern.Match(scanner.Bytes()) { + scanner.Scan() // these come in pairs + } else { + prunedStack = append(prunedStack, scanner.Text()) + scanner.Scan() // these come in pairs + prunedStack = append(prunedStack, scanner.Text()) + } + } + + return strings.Join(prunedStack, "\n") +} diff --git a/test/e2e/framework/log.go b/test/e2e/framework/log.go new file mode 100644 index 00000000..ff33f41d --- /dev/null +++ b/test/e2e/framework/log.go @@ -0,0 +1,94 @@ +package framework + +import ( + "bytes" + "fmt" + "regexp" + "runtime/debug" + "time" + + "github.com/onsi/ginkgo" + + e2eginkgowrapper "github.com/fatedier/frp/test/e2e/framework/ginkgowrapper" +) + +func nowStamp() string { + return time.Now().Format(time.StampMilli) +} + +func log(level string, format string, args ...interface{}) { + fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...) +} + +// Logf logs the info. +func Logf(format string, args ...interface{}) { + log("INFO", format, args...) +} + +// Failf logs the fail info, including a stack trace. +func Failf(format string, args ...interface{}) { + FailfWithOffset(1, format, args...) +} + +// FailfWithOffset calls "Fail" and logs the error with a stack trace that starts at "offset" levels above its caller +// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f"). +func FailfWithOffset(offset int, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + skip := offset + 1 + log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip)) + e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip) +} + +// Fail is a replacement for ginkgo.Fail which logs the problem as it occurs +// together with a stack trace and then calls ginkgowrapper.Fail. +func Fail(msg string, callerSkip ...int) { + skip := 1 + if len(callerSkip) > 0 { + skip += callerSkip[0] + } + log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip)) + e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip) +} + +var codeFilterRE = regexp.MustCompile(`/github.com/onsi/ginkgo/`) + +// PrunedStack is a wrapper around debug.Stack() that removes information +// about the current goroutine and optionally skips some of the initial stack entries. +// With skip == 0, the returned stack will start with the caller of PruneStack. +// From the remaining entries it automatically filters out useless ones like +// entries coming from Ginkgo. +// +// This is a modified copy of PruneStack in https://github.com/onsi/ginkgo/blob/f90f37d87fa6b1dd9625e2b1e83c23ffae3de228/internal/codelocation/code_location.go#L25: +// - simplified API and thus renamed (calls debug.Stack() instead of taking a parameter) +// - source code filtering updated to be specific to Kubernetes +// - optimized to use bytes and in-place slice filtering from +// https://github.com/golang/go/wiki/SliceTricks#filter-in-place +func PrunedStack(skip int) []byte { + fullStackTrace := debug.Stack() + stack := bytes.Split(fullStackTrace, []byte("\n")) + // Ensure that the even entries are the method names and the + // the odd entries the source code information. + if len(stack) > 0 && bytes.HasPrefix(stack[0], []byte("goroutine ")) { + // Ignore "goroutine 29 [running]:" line. + stack = stack[1:] + } + // The "+2" is for skipping over: + // - runtime/debug.Stack() + // - PrunedStack() + skip += 2 + if len(stack) > 2*skip { + stack = stack[2*skip:] + } + n := 0 + for i := 0; i < len(stack)/2; i++ { + // We filter out based on the source code file name. + if !codeFilterRE.Match([]byte(stack[i*2+1])) { + stack[n] = stack[i*2] + stack[n+1] = stack[i*2+1] + n += 2 + } + } + stack = stack[:n] + + return bytes.Join(stack, []byte("\n")) +} diff --git a/test/e2e/framework/mockservers.go b/test/e2e/framework/mockservers.go new file mode 100644 index 00000000..3598aac1 --- /dev/null +++ b/test/e2e/framework/mockservers.go @@ -0,0 +1,85 @@ +package framework + +import ( + "fmt" + "os" + + "github.com/fatedier/frp/test/e2e/mock/echoserver" + "github.com/fatedier/frp/test/e2e/pkg/port" +) + +const ( + TCPEchoServerPort = "TCPEchoServerPort" + UDPEchoServerPort = "UDPEchoServerPort" + UDSEchoServerAddr = "UDSEchoServerAddr" +) + +type MockServers struct { + tcpEchoServer *echoserver.Server + udpEchoServer *echoserver.Server + udsEchoServer *echoserver.Server +} + +func NewMockServers(portAllocator *port.Allocator) *MockServers { + s := &MockServers{} + tcpPort := portAllocator.Get() + udpPort := portAllocator.Get() + s.tcpEchoServer = echoserver.New(echoserver.Options{ + Type: echoserver.TCP, + BindAddr: "127.0.0.1", + BindPort: int32(tcpPort), + RepeatNum: 1, + }) + s.udpEchoServer = echoserver.New(echoserver.Options{ + Type: echoserver.UDP, + BindAddr: "127.0.0.1", + BindPort: int32(udpPort), + RepeatNum: 1, + }) + + udsIndex := portAllocator.Get() + udsAddr := fmt.Sprintf("%s/frp_echo_server_%d.sock", os.TempDir(), udsIndex) + os.Remove(udsAddr) + s.udsEchoServer = echoserver.New(echoserver.Options{ + Type: echoserver.Unix, + BindAddr: udsAddr, + RepeatNum: 1, + }) + return s +} + +func (m *MockServers) Run() error { + if err := m.tcpEchoServer.Run(); err != nil { + return err + } + if err := m.udpEchoServer.Run(); err != nil { + return err + } + if err := m.udsEchoServer.Run(); err != nil { + return err + } + return nil +} + +func (m *MockServers) Close() { + m.tcpEchoServer.Close() + m.udpEchoServer.Close() + m.udsEchoServer.Close() + os.Remove(m.udsEchoServer.GetOptions().BindAddr) +} + +func (m *MockServers) GetTemplateParams() map[string]interface{} { + ret := make(map[string]interface{}) + ret[TCPEchoServerPort] = m.tcpEchoServer.GetOptions().BindPort + ret[UDPEchoServerPort] = m.udpEchoServer.GetOptions().BindPort + ret[UDSEchoServerAddr] = m.udsEchoServer.GetOptions().BindAddr + return ret +} + +func (m *MockServers) GetParam(key string) interface{} { + params := m.GetTemplateParams() + if v, ok := params[key]; ok { + return v + } + return nil +} diff --git a/test/e2e/framework/process.go b/test/e2e/framework/process.go new file mode 100644 index 00000000..5be4572e --- /dev/null +++ b/test/e2e/framework/process.go @@ -0,0 +1,59 @@ +package framework + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "time" + + "github.com/fatedier/frp/test/e2e/pkg/process" + flog "github.com/fatedier/frp/utils/log" +) + +func GenerateConfigFile(path string, content string) error { + return ioutil.WriteFile(path, []byte(content), 0666) +} + +// RunProcesses run multiple processes from templates. +// The first template should always be frps. +func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []string) { + templates := make([]string, 0, len(serverTemplates)+len(clientTemplates)) + for _, t := range serverTemplates { + templates = append(templates, t) + } + for _, t := range clientTemplates { + templates = append(templates, t) + } + outs, ports, err := f.RenderTemplates(templates) + ExpectNoError(err) + ExpectTrue(len(templates) > 0) + + f.UsedPorts = ports + + for i := range serverTemplates { + path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i)) + err = ioutil.WriteFile(path, []byte(outs[i]), 0666) + ExpectNoError(err) + flog.Trace("[%s] %s", path, outs[i]) + p := process.New(TestContext.FRPServerPath, []string{"-c", path}) + f.serverConfPaths = append(f.serverConfPaths, path) + f.serverProcesses = append(f.serverProcesses, p) + err = p.Start() + ExpectNoError(err) + time.Sleep(500 * time.Millisecond) + } + + for i := range clientTemplates { + index := i + len(serverTemplates) + path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i)) + err = ioutil.WriteFile(path, []byte(outs[index]), 0666) + ExpectNoError(err) + flog.Trace("[%s] %s", path, outs[index]) + p := process.New(TestContext.FRPClientPath, []string{"-c", path}) + f.clientConfPaths = append(f.clientConfPaths, path) + f.clientProcesses = append(f.clientProcesses, p) + err = p.Start() + ExpectNoError(err) + time.Sleep(500 * time.Millisecond) + } +} diff --git a/test/e2e/framework/request.go b/test/e2e/framework/request.go new file mode 100644 index 00000000..72ae7ba3 --- /dev/null +++ b/test/e2e/framework/request.go @@ -0,0 +1,51 @@ +package framework + +import ( + "time" + + "github.com/fatedier/frp/test/e2e/pkg/request" +) + +func ExpectRequest(protocol string, port int, in, out []byte, timeout time.Duration, explain ...interface{}) { + switch protocol { + case "tcp": + ExpectTCPRequest(port, in, out, timeout, explain...) + case "udp": + ExpectUDPRequest(port, in, out, timeout, explain...) + default: + Failf("ExpectRequest not support protocol: %s", protocol) + } +} + +func ExpectRequestError(protocol string, port int, in []byte, timeout time.Duration, explain ...interface{}) { + switch protocol { + case "tcp": + ExpectTCPRequestError(port, in, timeout, explain...) + case "udp": + ExpectUDPRequestError(port, in, timeout, explain...) + default: + Failf("ExpectRequestError not support protocol: %s", protocol) + } +} + +func ExpectTCPRequest(port int, in, out []byte, timeout time.Duration, explain ...interface{}) { + res, err := request.SendTCPRequest(port, in, timeout) + ExpectNoError(err, explain...) + ExpectEqual(string(out), res, explain...) +} + +func ExpectTCPRequestError(port int, in []byte, timeout time.Duration, explain ...interface{}) { + _, err := request.SendTCPRequest(port, in, timeout) + ExpectError(err, explain...) +} + +func ExpectUDPRequest(port int, in, out []byte, timeout time.Duration, explain ...interface{}) { + res, err := request.SendUDPRequest(port, in, timeout) + ExpectNoError(err, explain...) + ExpectEqual(string(out), res, explain...) +} + +func ExpectUDPRequestError(port int, in []byte, timeout time.Duration, explain ...interface{}) { + _, err := request.SendUDPRequest(port, in, timeout) + ExpectError(err, explain...) +} diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go new file mode 100644 index 00000000..958c78f8 --- /dev/null +++ b/test/e2e/framework/test_context.go @@ -0,0 +1,52 @@ +package framework + +import ( + "flag" + "fmt" + "os" + + "github.com/onsi/ginkgo/config" +) + +type TestContextType struct { + FRPClientPath string + FRPServerPath string + LogLevel string + Debug bool +} + +var TestContext TestContextType + +// RegisterCommonFlags registers flags common to all e2e test suites. +// The flag set can be flag.CommandLine (if desired) or a custom +// flag set that then gets passed to viperconfig.ViperizeFlags. +// +// The other Register*Flags methods below can be used to add more +// test-specific flags. However, those settings then get added +// regardless whether the test is actually in the test suite. +// +func RegisterCommonFlags(flags *flag.FlagSet) { + // Turn on EmitSpecProgress to get spec progress (especially on interrupt) + config.GinkgoConfig.EmitSpecProgress = true + + // Randomize specs as well as suites + config.GinkgoConfig.RandomizeAllSpecs = true + + flags.StringVar(&TestContext.FRPClientPath, "frpc-path", "../../bin/frpc", "The frp client binary to use.") + flags.StringVar(&TestContext.FRPServerPath, "frps-path", "../../bin/frps", "The frp server binary to use.") + flags.StringVar(&TestContext.LogLevel, "log-level", "debug", "Log level.") + flags.BoolVar(&TestContext.Debug, "debug", false, "Enable debug mode to print detail info.") +} + +func ValidateTestContext(t *TestContextType) error { + if t.FRPClientPath == "" || t.FRPServerPath == "" { + return fmt.Errorf("frpc and frps binary path can't be empty") + } + if _, err := os.Stat(t.FRPClientPath); err != nil { + return fmt.Errorf("load frpc-path error: %v", err) + } + if _, err := os.Stat(t.FRPServerPath); err != nil { + return fmt.Errorf("load frps-path error: %v", err) + } + return nil +} diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go new file mode 100644 index 00000000..9c2ae83f --- /dev/null +++ b/test/e2e/framework/util.go @@ -0,0 +1,18 @@ +package framework + +import ( + "github.com/google/uuid" +) + +// RunID is a unique identifier of the e2e run. +// Beware that this ID is not the same for all tests in the e2e run, because each Ginkgo node creates it separately. +var RunID string + +func init() { + uuid, _ := uuid.NewUUID() + RunID = uuid.String() +} + +func GenPortName(name string) string { + return "Port" + name +} diff --git a/test/e2e/mock/echoserver/echoserver.go b/test/e2e/mock/echoserver/echoserver.go new file mode 100644 index 00000000..9d51490e --- /dev/null +++ b/test/e2e/mock/echoserver/echoserver.go @@ -0,0 +1,111 @@ +package echoserver + +import ( + "fmt" + "net" + "strings" + + fnet "github.com/fatedier/frp/utils/net" +) + +type ServerType string + +const ( + TCP ServerType = "tcp" + UDP ServerType = "udp" + Unix ServerType = "unix" +) + +type Options struct { + Type ServerType + BindAddr string + BindPort int32 + RepeatNum int + SpecifiedResponse string +} + +type Server struct { + opt Options + + l net.Listener +} + +func New(opt Options) *Server { + if opt.Type == "" { + opt.Type = TCP + } + if opt.BindAddr == "" { + opt.BindAddr = "127.0.0.1" + } + if opt.RepeatNum <= 0 { + opt.RepeatNum = 1 + } + return &Server{ + opt: opt, + } +} + +func (s *Server) GetOptions() Options { + return s.opt +} + +func (s *Server) Run() error { + if err := s.initListener(); err != nil { + return err + } + + go func() { + for { + c, err := s.l.Accept() + if err != nil { + return + } + go s.handle(c) + } + }() + return nil +} + +func (s *Server) Close() error { + if s.l != nil { + return s.l.Close() + } + return nil +} + +func (s *Server) initListener() (err error) { + switch s.opt.Type { + case TCP: + s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.opt.BindAddr, s.opt.BindPort)) + case UDP: + s.l, err = fnet.ListenUDP(s.opt.BindAddr, int(s.opt.BindPort)) + case Unix: + s.l, err = net.Listen("unix", s.opt.BindAddr) + default: + return fmt.Errorf("unknown server type: %s", s.opt.Type) + } + if err != nil { + return + } + return nil +} + +func (s *Server) handle(c net.Conn) { + defer c.Close() + + buf := make([]byte, 2048) + for { + n, err := c.Read(buf) + if err != nil { + return + } + + var response string + if len(s.opt.SpecifiedResponse) > 0 { + response = s.opt.SpecifiedResponse + } else { + response = strings.Repeat(string(buf[:n]), s.opt.RepeatNum) + } + c.Write([]byte(response)) + } +} diff --git a/test/e2e/pkg/port/port.go b/test/e2e/pkg/port/port.go new file mode 100644 index 00000000..e8ccd911 --- /dev/null +++ b/test/e2e/pkg/port/port.go @@ -0,0 +1,57 @@ +package port + +import ( + "fmt" + "net" + + "k8s.io/apimachinery/pkg/util/sets" +) + +type Allocator struct { + reserved sets.Int + used sets.Int +} + +// NewAllocator return a port allocator for testing. +// Example: from: 10, to: 20, mod 4, index 1 +// Reserved ports: 13, 17 +func NewAllocator(from int, to int, mod int, index int) *Allocator { + pa := &Allocator{ + reserved: sets.NewInt(), + used: sets.NewInt(), + } + for i := from; i <= to; i++ { + if i%mod == index { + pa.reserved.Insert(i) + } + } + return pa +} + +func (pa *Allocator) Get() int { + for i := 0; i < 10; i++ { + port, _ := pa.reserved.PopAny() + if port == 0 { + return 0 + } + + // TODO: Distinguish between TCP and UDP + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + // Maybe not controlled by us, mark it used. + pa.used.Insert(port) + continue + } + l.Close() + pa.used.Insert(port) + return port + } + return 0 +} + +func (pa *Allocator) Release(port int) { + if pa.used.Has(port) { + pa.used.Delete(port) + pa.reserved.Insert(port) + } +} diff --git a/test/e2e/pkg/process/process.go b/test/e2e/pkg/process/process.go new file mode 100644 index 00000000..6417276d --- /dev/null +++ b/test/e2e/pkg/process/process.go @@ -0,0 +1,54 @@ +package process + +import ( + "bytes" + "context" + "os/exec" +) + +type Process struct { + cmd *exec.Cmd + cancel context.CancelFunc + errorOutput *bytes.Buffer + stdOutput *bytes.Buffer + + beforeStopHandler func() +} + +func New(path string, params []string) *Process { + ctx, cancel := context.WithCancel(context.Background()) + cmd := exec.CommandContext(ctx, path, params...) + p := &Process{ + cmd: cmd, + cancel: cancel, + } + p.errorOutput = bytes.NewBufferString("") + p.stdOutput = bytes.NewBufferString("") + cmd.Stderr = p.errorOutput + cmd.Stdout = p.stdOutput + return p +} + +func (p *Process) Start() error { + return p.cmd.Start() +} + +func (p *Process) Stop() error { + if p.beforeStopHandler != nil { + p.beforeStopHandler() + } + p.cancel() + return p.cmd.Wait() +} + +func (p *Process) ErrorOutput() string { + return p.errorOutput.String() +} + +func (p *Process) StdOutput() string { + return p.stdOutput.String() +} + +func (p *Process) SetBeforeStopHandler(fn func()) { + p.beforeStopHandler = fn +} diff --git a/test/e2e/pkg/request/request.go b/test/e2e/pkg/request/request.go new file mode 100644 index 00000000..cc4c347d --- /dev/null +++ b/test/e2e/pkg/request/request.go @@ -0,0 +1,40 @@ +package request + +import ( + "fmt" + "net" + "time" +) + +func SendTCPRequest(port int, content []byte, timeout time.Duration) (string, error) { + c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + return "", fmt.Errorf("connect to tcp server error: %v", err) + } + defer c.Close() + + c.SetDeadline(time.Now().Add(timeout)) + return sendRequestByConn(c, content) +} + +func SendUDPRequest(port int, content []byte, timeout time.Duration) (string, error) { + c, err := net.Dial("udp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + return "", fmt.Errorf("connect to udp server error: %v", err) + } + defer c.Close() + + c.SetDeadline(time.Now().Add(timeout)) + return sendRequestByConn(c, content) +} + +func sendRequestByConn(c net.Conn, content []byte) (string, error) { + c.Write(content) + + buf := make([]byte, 2048) + n, err := c.Read(buf) + if err != nil { + return "", fmt.Errorf("read error: %v", err) + } + return string(buf[:n]), nil +} diff --git a/test/e2e/plugin/client_plugins.go b/test/e2e/plugin/client_plugins.go new file mode 100644 index 00000000..0c612982 --- /dev/null +++ b/test/e2e/plugin/client_plugins.go @@ -0,0 +1,76 @@ +package plugin + +import ( + "fmt" + "time" + + "github.com/fatedier/frp/test/e2e/framework" + "github.com/fatedier/frp/test/e2e/framework/consts" + + . "github.com/onsi/ginkgo" +) + +var connTimeout = 2 * time.Second + +var _ = Describe("[Feature: Client-Plugins]", func() { + f := framework.NewDefaultFramework() + + Describe("UnixDomainSocket", func() { + It("Expose a unix domain socket echo server", func() { + serverConf := consts.DefaultServerConfig + clientConf := consts.DefaultClientConfig + + getProxyConf := func(proxyName string, portName string, extra string) string { + return fmt.Sprintf(` + [%s] + type = tcp + remote_port = {{ .%s }} + plugin = unix_domain_socket + plugin_unix_path = {{ .%s }} + `+extra, proxyName, portName, framework.UDSEchoServerAddr) + } + + tests := []struct { + proxyName string + portName string + extraConfig string + }{ + { + proxyName: "normal", + portName: framework.GenPortName("Normal"), + }, + { + proxyName: "with-encryption", + portName: framework.GenPortName("WithEncryption"), + extraConfig: "use_encryption = true", + }, + { + proxyName: "with-compression", + portName: framework.GenPortName("WithCompression"), + extraConfig: "use_compression = true", + }, + { + proxyName: "with-encryption-and-compression", + portName: framework.GenPortName("WithEncryptionAndCompression"), + extraConfig: ` + use_encryption = true + use_compression = true + `, + }, + } + + // build all client config + for _, test := range tests { + clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n" + } + // run frps and frpc + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + for _, test := range tests { + framework.ExpectTCPRequest(f.UsedPorts[test.portName], + []byte(consts.TestString), []byte(consts.TestString), + connTimeout, test.proxyName) + } + }) + }) +}) diff --git a/test/e2e/suites.go b/test/e2e/suites.go new file mode 100644 index 00000000..268dcb45 --- /dev/null +++ b/test/e2e/suites.go @@ -0,0 +1,16 @@ +package e2e + +// CleanupSuite is the boilerplate that can be used after tests on ginkgo were run, on the SynchronizedAfterSuite step. +// Similar to SynchronizedBeforeSuite, we want to run some operations only once (such as collecting cluster logs). +// Here, the order of functions is reversed; first, the function which runs everywhere, +// and then the function that only runs on the first Ginkgo node. +func CleanupSuite() { + // Run on all Ginkgo nodes + // TODO +} + +// AfterSuiteActions are actions that are run on ginkgo's SynchronizedAfterSuite +func AfterSuiteActions() { + // Run only Ginkgo on node 1 + // TODO +} diff --git a/tests/ci/auth_test.go b/tests/ci/auth_test.go deleted file mode 100644 index 31074839..00000000 --- a/tests/ci/auth_test.go +++ /dev/null @@ -1,72 +0,0 @@ -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_TOKEN_TCP_CONF = ` -[common] -bind_addr = 0.0.0.0 -bind_port = 20000 -log_file = console -log_level = debug -authentication_method = token -token = 123456 -` - -const FRPC_TOKEN_TCP_CONF = ` -[common] -server_addr = 127.0.0.1 -server_port = 20000 -log_file = console -log_level = debug -authentication_method = token -token = 123456 -protocol = tcp - -[tcp] -type = tcp -local_port = 10701 -remote_port = 20801 -` - -func TestTcpWithTokenAuthentication(t *testing.T) { - assert := assert.New(t) - frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TOKEN_TCP_CONF) - if assert.NoError(err) { - defer os.Remove(frpsCfgPath) - } - - frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TOKEN_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) -} diff --git a/tests/ci/cmd_test.go b/tests/ci/cmd_test.go index 44f2e9e8..8a0b4775 100644 --- a/tests/ci/cmd_test.go +++ b/tests/ci/cmd_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCmdTcp(t *testing.T) { +func TestCmdTCP(t *testing.T) { assert := assert.New(t) var err error @@ -29,12 +29,12 @@ func TestCmdTcp(t *testing.T) { } time.Sleep(500 * time.Millisecond) - res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) } -func TestCmdUdp(t *testing.T) { +func TestCmdUDP(t *testing.T) { assert := assert.New(t) var err error @@ -53,12 +53,12 @@ func TestCmdUdp(t *testing.T) { } time.Sleep(500 * time.Millisecond) - res, err := util.SendUdpMsg("127.0.0.1:20802", consts.TEST_UDP_ECHO_STR) + res, err := util.SendUDPMsg("127.0.0.1:20802", consts.TEST_UDP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_UDP_ECHO_STR, res) } -func TestCmdHttp(t *testing.T) { +func TestCmdHTTP(t *testing.T) { assert := assert.New(t) var err error @@ -77,7 +77,7 @@ func TestCmdHttp(t *testing.T) { } time.Sleep(500 * time.Millisecond) - code, body, _, err := util.SendHttpMsg("GET", "http://127.0.0.1:20001", "", nil, "") + code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:20001", "", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) diff --git a/tests/ci/health/health_test.go b/tests/ci/health/health_test.go index f19c3750..0f48f463 100644 --- a/tests/ci/health/health_test.go +++ b/tests/ci/health/health_test.go @@ -102,7 +102,7 @@ func TestHealthCheck(t *testing.T) { var healthMu sync.RWMutex svc1Health := true svc2Health := true - httpSvc1 := mock.NewHttpServer(15003, func(w http.ResponseWriter, r *http.Request) { + httpSvc1 := mock.NewHTTPServer(15003, func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.Path, "health") { healthMu.RLock() defer healthMu.RUnlock() @@ -120,7 +120,7 @@ func TestHealthCheck(t *testing.T) { defer httpSvc1.Stop() } - httpSvc2 := mock.NewHttpServer(15004, func(w http.ResponseWriter, r *http.Request) { + httpSvc2 := mock.NewHTTPServer(15004, func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.Path, "health") { healthMu.RLock() defer healthMu.RUnlock() @@ -138,7 +138,7 @@ func TestHealthCheck(t *testing.T) { defer httpSvc2.Stop() } - httpSvc3 := mock.NewHttpServer(15005, func(w http.ResponseWriter, r *http.Request) { + httpSvc3 := mock.NewHTTPServer(15005, func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("http3")) }) err = httpSvc3.Start() @@ -146,7 +146,7 @@ func TestHealthCheck(t *testing.T) { defer httpSvc3.Stop() } - httpSvc4 := mock.NewHttpServer(15006, func(w http.ResponseWriter, r *http.Request) { + httpSvc4 := mock.NewHTTPServer(15006, func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("http4")) }) err = httpSvc4.Start() @@ -185,11 +185,11 @@ func TestHealthCheck(t *testing.T) { // ****** healcheck type tcp ****** // echo1 and echo2 is ok result := make([]string, 0) - res, err := util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err := util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) - res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err = util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) @@ -201,11 +201,11 @@ func TestHealthCheck(t *testing.T) { time.Sleep(1200 * time.Millisecond) result = make([]string, 0) - res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err = util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) - res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err = util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) @@ -221,11 +221,11 @@ func TestHealthCheck(t *testing.T) { time.Sleep(1200 * time.Millisecond) result = make([]string, 0) - res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err = util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) - res, err = util.SendTcpMsg("127.0.0.1:15000", "echo") + res, err = util.SendTCPMsg("127.0.0.1:15000", "echo") assert.NoError(err) result = append(result, res) @@ -234,12 +234,12 @@ func TestHealthCheck(t *testing.T) { // ****** healcheck type http ****** // http1 and http2 is ok - code, body, _, err := util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + code, body, _, err := util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") assert.NoError(err) assert.Equal(200, code) assert.Equal("http1", body) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") assert.NoError(err) assert.Equal(200, code) assert.Equal("http2", body) @@ -250,12 +250,12 @@ func TestHealthCheck(t *testing.T) { healthMu.Unlock() time.Sleep(1200 * time.Millisecond) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") assert.NoError(err) assert.Equal(200, code) assert.Equal("http1", body) - code, _, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + code, _, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") assert.NoError(err) assert.Equal(404, code) @@ -265,12 +265,12 @@ func TestHealthCheck(t *testing.T) { healthMu.Unlock() time.Sleep(1200 * time.Millisecond) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test1.com", nil, "") assert.NoError(err) assert.Equal(200, code) assert.Equal("http1", body) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test2.com", nil, "") assert.NoError(err) assert.Equal(200, code) assert.Equal("http2", body) @@ -278,12 +278,12 @@ func TestHealthCheck(t *testing.T) { // ****** load balancing type http ****** result = make([]string, 0) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "") assert.NoError(err) assert.Equal(200, code) result = append(result, body) - code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "") assert.NoError(err) assert.Equal(200, code) result = append(result, body) diff --git a/tests/ci/normal_test.go b/tests/ci/normal_test.go index 6f1a8ade..e9e34945 100644 --- a/tests/ci/normal_test.go +++ b/tests/ci/normal_test.go @@ -33,9 +33,9 @@ func TestMain(m *testing.M) { panic(err) } - go mock.StartUdpEchoServer(consts.TEST_UDP_PORT) + go mock.StartUDPEchoServer(consts.TEST_UDP_PORT) go mock.StartUnixDomainServer(consts.TEST_UNIX_DOMAIN_ADDR) - go mock.StartHttpServer(consts.TEST_HTTP_PORT) + go mock.StartHTTPServer(consts.TEST_HTTP_PORT) p1 := util.NewProcess(consts.FRPS_BIN_PATH, []string{"-c", "./auto_test_frps.ini"}) if err = p1.Start(); err != nil { @@ -61,131 +61,64 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -func TestTcp(t *testing.T) { - assert := assert.New(t) - // Normal - addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_FRP_PORT) - res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) - assert.NoError(err) - assert.Equal(consts.TEST_TCP_ECHO_STR, res) - - // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_EC_FRP_PORT) - res, err = util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) - assert.NoError(err) - assert.Equal(consts.TEST_TCP_ECHO_STR, res) -} - -func TestUdp(t *testing.T) { - assert := assert.New(t) - // Normal - addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_UDP_FRP_PORT) - res, err := util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) - assert.NoError(err) - assert.Equal(consts.TEST_UDP_ECHO_STR, res) - - // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_UDP_EC_FRP_PORT) - res, err = util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) - assert.NoError(err) - assert.Equal(consts.TEST_UDP_ECHO_STR, res) -} - -func TestUnixDomain(t *testing.T) { - assert := assert.New(t) - // Normal - addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_UNIX_DOMAIN_FRP_PORT) - res, err := util.SendTcpMsg(addr, consts.TEST_UNIX_DOMAIN_STR) - if assert.NoError(err) { - assert.Equal(consts.TEST_UNIX_DOMAIN_STR, res) - } -} - -func TestStcp(t *testing.T) { - assert := assert.New(t) - // Normal - addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_STCP_FRP_PORT) - res, err := util.SendTcpMsg(addr, consts.TEST_STCP_ECHO_STR) - if assert.NoError(err) { - assert.Equal(consts.TEST_STCP_ECHO_STR, res) - } - - // Encrytion and compression - addr = fmt.Sprintf("127.0.0.1:%d", consts.TEST_STCP_EC_FRP_PORT) - res, err = util.SendTcpMsg(addr, consts.TEST_STCP_ECHO_STR) - if assert.NoError(err) { - assert.Equal(consts.TEST_STCP_ECHO_STR, res) - } -} - -func TestSudp(t *testing.T) { - assert := assert.New(t) - // Normal - addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_SUDP_FRP_PORT) - res, err := util.SendUdpMsg(addr, consts.TEST_SUDP_ECHO_STR) - - assert.NoError(err) - assert.Equal(consts.TEST_SUDP_ECHO_STR, res) -} - -func TestHttp(t *testing.T) { +func TestHTTP(t *testing.T) { assert := assert.New(t) // web01 - code, body, _, err := util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "") + code, body, _, err := util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // web02 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test2.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test2.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // error host header - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "errorhost.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "errorhost.frp.com", nil, "") if assert.NoError(err) { assert.Equal(404, code) } // web03 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/foo", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/foo", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_FOO_STR, body) } // web04 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/bar", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d/bar", consts.TEST_HTTP_FRP_PORT), "test3.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_BAR_STR, body) } // web05 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", nil, "") if assert.NoError(err) { assert.Equal(401, code) } headers := make(map[string]string) headers["Authorization"] = util.BasicAuth("test", "test") - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", headers, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test5.frp.com", headers, "") if assert.NoError(err) { assert.Equal(401, code) } // web06 var header http.Header - code, body, header, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test6.frp.com", nil, "") + code, body, header, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test6.frp.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) @@ -194,40 +127,40 @@ func TestHttp(t *testing.T) { // wildcard_http // test.frp1.com match *.frp1.com - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test.frp1.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test.frp1.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // new.test.frp1.com also match *.frp1.com - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "new.test.frp1.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "new.test.frp1.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal(consts.TEST_HTTP_NORMAL_STR, body) } // subhost01 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test01.sub.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test01.sub.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal("test01.sub.com", body) } // subhost02 - code, body, _, err = util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test02.sub.com", nil, "") + code, body, _, err = util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "test02.sub.com", nil, "") if assert.NoError(err) { assert.Equal(200, code) assert.Equal("test02.sub.com", body) } } -func TestTcpMux(t *testing.T) { +func TestTCPMux(t *testing.T) { assert := assert.New(t) conn, err := gnet.DialTcpByProxy(fmt.Sprintf("http://%s:%d", "127.0.0.1", consts.TEST_TCP_MUX_FRP_PORT), "tunnel1") if assert.NoError(err) { - res, err := util.SendTcpMsgByConn(conn, consts.TEST_TCP_ECHO_STR) + res, err := util.SendTCPMsgByConn(conn, consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) } @@ -252,66 +185,66 @@ func TestWebSocket(t *testing.T) { func TestAllowPorts(t *testing.T) { assert := assert.New(t) // Port not allowed - status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNotAllowed) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTCPPortNotAllowed) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyPhaseStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } - status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNotAllowed) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUDPPortNotAllowed) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyPhaseStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortNotAllowed.Error())) } - status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortUnavailable) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTCPPortUnavailable) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusStartErr, status.Status) + assert.Equal(proxy.ProxyPhaseStartErr, status.Status) assert.True(strings.Contains(status.Err, ports.ErrPortUnAvailable.Error())) } // Port normal - status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpPortNormal) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTCPPortNormal) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyPhaseRunning, status.Status) } - status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpPortNormal) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUDPPortNormal) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyPhaseRunning, status.Status) } } func TestRandomPort(t *testing.T) { assert := assert.New(t) // tcp - status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTcpRandomPort) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyTCPRandomPort) if assert.NoError(err) { addr := status.RemoteAddr - res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) + res, err := util.SendTCPMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) } // udp - status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUdpRandomPort) + status, err = util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyUDPRandomPort) if assert.NoError(err) { addr := status.RemoteAddr - res, err := util.SendUdpMsg(addr, consts.TEST_UDP_ECHO_STR) + res, err := util.SendUDPMsg(addr, consts.TEST_UDP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_UDP_ECHO_STR, res) } } -func TestPluginHttpProxy(t *testing.T) { +func TestPluginHTTPProxy(t *testing.T) { assert := assert.New(t) - status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyHttpProxy) + status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, consts.ProxyHTTPProxy) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyPhaseRunning, status.Status) // http proxy addr := status.RemoteAddr - code, body, _, err := util.SendHttpMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), + code, body, _, err := util.SendHTTPMsg("GET", fmt.Sprintf("http://127.0.0.1:%d", consts.TEST_HTTP_FRP_PORT), "", nil, "http://"+addr) if assert.NoError(err) { assert.Equal(200, code) @@ -321,7 +254,7 @@ func TestPluginHttpProxy(t *testing.T) { // connect method conn, err := gnet.DialTcpByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP_FRP_PORT)) if assert.NoError(err) { - res, err := util.SendTcpMsgByConn(conn, consts.TEST_TCP_ECHO_STR) + res, err := util.SendTCPMsgByConn(conn, consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) } @@ -332,10 +265,10 @@ func TestRangePortsMapping(t *testing.T) { assert := assert.New(t) for i := 0; i < 3; i++ { - name := fmt.Sprintf("%s_%d", consts.ProxyRangeTcpPrefix, i) + name := fmt.Sprintf("%s_%d", consts.ProxyRangeTCPPrefix, i) status, err := util.GetProxyStatus(consts.ADMIN_ADDR, consts.ADMIN_USER, consts.ADMIN_PWD, name) if assert.NoError(err) { - assert.Equal(proxy.ProxyStatusRunning, status.Status) + assert.Equal(proxy.ProxyPhaseRunning, status.Status) } } } @@ -350,7 +283,7 @@ func TestGroup(t *testing.T) { addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_TCP2_FRP_PORT) for i := 0; i < 6; i++ { - res, err := util.SendTcpMsg(addr, consts.TEST_TCP_ECHO_STR) + res, err := util.SendTCPMsg(addr, consts.TEST_TCP_ECHO_STR) assert.NoError(err) switch res { case consts.TEST_TCP_ECHO_STR: diff --git a/tests/ci/reconnect_test.go b/tests/ci/reconnect_test.go index 9fe53b5e..86ab317b 100644 --- a/tests/ci/reconnect_test.go +++ b/tests/ci/reconnect_test.go @@ -66,7 +66,7 @@ func TestReconnect(t *testing.T) { time.Sleep(500 * time.Millisecond) // test tcp - res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) @@ -75,7 +75,7 @@ func TestReconnect(t *testing.T) { time.Sleep(200 * time.Millisecond) // test tcp, expect failed - _, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + _, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) assert.Error(err) // restart frpc @@ -87,7 +87,7 @@ func TestReconnect(t *testing.T) { time.Sleep(500 * time.Millisecond) // test tcp - res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) @@ -96,7 +96,7 @@ func TestReconnect(t *testing.T) { time.Sleep(200 * time.Millisecond) // test tcp, expect failed - _, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + _, err = util.SendTCPMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) assert.Error(err) // restart frps @@ -109,7 +109,7 @@ func TestReconnect(t *testing.T) { time.Sleep(2 * time.Second) // test tcp - res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) } diff --git a/tests/ci/reload_test.go b/tests/ci/reload_test.go index 4c5b4d45..73a7a091 100644 --- a/tests/ci/reload_test.go +++ b/tests/ci/reload_test.go @@ -105,17 +105,17 @@ func TestReload(t *testing.T) { time.Sleep(500 * time.Millisecond) // test tcp1 - res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) // test tcp2 - res, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) + res, err = util.SendTCPMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) // test tcp3 - res, err = util.SendTcpMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) + res, err = util.SendTCPMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) @@ -131,20 +131,20 @@ func TestReload(t *testing.T) { time.Sleep(time.Second) // test tcp1 - res, err = util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) // test origin tcp2, expect failed - res, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) + res, err = util.SendTCPMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) assert.Error(err) // test new origin tcp2 with different port - res, err = util.SendTcpMsg("127.0.0.1:20902", consts.TEST_TCP_ECHO_STR) + res, err = util.SendTCPMsg("127.0.0.1:20902", consts.TEST_TCP_ECHO_STR) assert.NoError(err) assert.Equal(consts.TEST_TCP_ECHO_STR, res) // test tcp3, expect failed - res, err = util.SendTcpMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) + res, err = util.SendTCPMsg("127.0.0.1:20803", consts.TEST_TCP_ECHO_STR) assert.Error(err) } diff --git a/tests/ci/template_test.go b/tests/ci/template_test.go index 1e30af52..f1361212 100644 --- a/tests/ci/template_test.go +++ b/tests/ci/template_test.go @@ -66,7 +66,7 @@ func TestConfTemplate(t *testing.T) { time.Sleep(500 * time.Millisecond) // test tcp1 - res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + 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) } diff --git a/tests/ci/tls_test.go b/tests/ci/tls_test.go deleted file mode 100644 index c46e9fa7..00000000 --- a/tests/ci/tls_test.go +++ /dev/null @@ -1,280 +0,0 @@ -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) -} - -const FRPS_TLS_ONLY_TCP_CONF = ` -[common] -bind_addr = 0.0.0.0 -bind_port = 20000 -log_file = console -log_level = debug -token = 123456 -tls_only = true -` - -const FRPC_TLS_ONLY_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 -` - -const FRPC_TLS_ONLY_NO_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 = false - -[tcp] -type = tcp -local_port = 10701 -remote_port = 20802 -` - -func TestTlsOnlyOverTCP(t *testing.T) { - assert := assert.New(t) - frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TLS_ONLY_TCP_CONF) - if assert.NoError(err) { - defer os.Remove(frpsCfgPath) - } - - frpcWithTlsCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_ONLY_TCP_CONF) - if assert.NoError(err) { - defer os.Remove(frpcWithTlsCfgPath) - } - - 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) - - frpcProcessWithTls := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcWithTlsCfgPath}) - err = frpcProcessWithTls.Start() - if assert.NoError(err) { - defer frpcProcessWithTls.Stop() - } - time.Sleep(500 * time.Millisecond) - - // test tcp over tls - 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) - frpcProcessWithTls.Stop() - - frpcWithoutTlsCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TLS_ONLY_NO_TLS_TCP_CONF) - if assert.NoError(err) { - defer os.Remove(frpcWithTlsCfgPath) - } - - frpcProcessWithoutTls := util.NewProcess(consts.FRPC_BIN_PATH, []string{"-c", frpcWithoutTlsCfgPath}) - err = frpcProcessWithoutTls.Start() - if assert.NoError(err) { - defer frpcProcessWithoutTls.Stop() - } - time.Sleep(500 * time.Millisecond) - - // test tcp without tls - _, err = util.SendTcpMsg("127.0.0.1:20802", consts.TEST_TCP_ECHO_STR) - assert.Error(err) -} diff --git a/tests/consts/consts.go b/tests/consts/consts.go index e7f970e4..ebe00c13 100644 --- a/tests/consts/consts.go +++ b/tests/consts/consts.go @@ -49,16 +49,16 @@ var ( TEST_SUDP_FRP_PORT int = 10816 TEST_SUDP_ECHO_STR string = "sudp type:" + TEST_STR - ProxyTcpPortNotAllowed string = "tcp_port_not_allowed" - ProxyTcpPortUnavailable string = "tcp_port_unavailable" - ProxyTcpPortNormal string = "tcp_port_normal" - ProxyTcpRandomPort string = "tcp_random_port" - ProxyUdpPortNotAllowed string = "udp_port_not_allowed" - ProxyUdpPortNormal string = "udp_port_normal" - ProxyUdpRandomPort string = "udp_random_port" - ProxyHttpProxy string = "http_proxy" + ProxyTCPPortNotAllowed string = "tcp_port_not_allowed" + ProxyTCPPortUnavailable string = "tcp_port_unavailable" + ProxyTCPPortNormal string = "tcp_port_normal" + ProxyTCPRandomPort string = "tcp_random_port" + ProxyUDPPortNotAllowed string = "udp_port_not_allowed" + ProxyUDPPortNormal string = "udp_port_normal" + ProxyUDPRandomPort string = "udp_random_port" + ProxyHTTPProxy string = "http_proxy" - ProxyRangeTcpPrefix string = "range_tcp" + ProxyRangeTCPPrefix string = "range_tcp" ) func init() { diff --git a/tests/mock/echo_server.go b/tests/mock/echo_server.go index 5ae40ee9..ad49a558 100644 --- a/tests/mock/echo_server.go +++ b/tests/mock/echo_server.go @@ -54,7 +54,7 @@ func (es *EchoServer) Stop() { es.l.Close() } -func StartUdpEchoServer(port int) { +func StartUDPEchoServer(port int) { l, err := frpNet.ListenUDP("127.0.0.1", port) if err != nil { fmt.Printf("udp echo server listen error: %v\n", err) diff --git a/tests/mock/http_server.go b/tests/mock/http_server.go index 92c51a6c..56fb51c5 100644 --- a/tests/mock/http_server.go +++ b/tests/mock/http_server.go @@ -13,21 +13,21 @@ import ( "github.com/gorilla/websocket" ) -type HttpServer struct { +type HTTPServer struct { l net.Listener port int handler http.HandlerFunc } -func NewHttpServer(port int, handler http.HandlerFunc) *HttpServer { - return &HttpServer{ +func NewHTTPServer(port int, handler http.HandlerFunc) *HTTPServer { + return &HTTPServer{ port: port, handler: handler, } } -func (hs *HttpServer) Start() error { +func (hs *HTTPServer) Start() error { l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", hs.port)) if err != nil { fmt.Printf("http server listen error: %v\n", err) @@ -39,14 +39,14 @@ func (hs *HttpServer) Start() error { return nil } -func (hs *HttpServer) Stop() { +func (hs *HTTPServer) Stop() { hs.l.Close() } var upgrader = websocket.Upgrader{} -func StartHttpServer(port int) { - http.HandleFunc("/", handleHttp) +func StartHTTPServer(port int) { + http.HandleFunc("/", handleHTTP) http.HandleFunc("/ws", handleWebSocket) http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil) } @@ -71,7 +71,7 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { } } -func handleHttp(w http.ResponseWriter, r *http.Request) { +func handleHTTP(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-From-Where") == "frp" { w.Header().Set("X-Header-Set", "true") } diff --git a/tests/util/util.go b/tests/util/util.go index 3dfd28f9..377965c4 100644 --- a/tests/util/util.go +++ b/tests/util/util.go @@ -41,37 +41,37 @@ func GetProxyStatus(statusAddr string, user string, passwd string, name string) if err != nil { return status, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) } - for _, s := range allStatus.Tcp { + for _, s := range allStatus.TCP { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Udp { + for _, s := range allStatus.UDP { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Http { + for _, s := range allStatus.HTTP { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Https { + for _, s := range allStatus.HTTPS { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Stcp { + for _, s := range allStatus.STCP { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Xtcp { + for _, s := range allStatus.XTCP { if s.Name == name { return &s, nil } } - for _, s := range allStatus.Sudp { + for _, s := range allStatus.SUDP { if s.Name == name { return &s, nil } @@ -101,17 +101,17 @@ func ReloadConf(reloadAddr string, user string, passwd string) error { return nil } -func SendTcpMsg(addr string, msg string) (res string, err error) { +func SendTCPMsg(addr string, msg string) (res string, err error) { c, err := net.Dial("tcp", addr) if err != nil { err = fmt.Errorf("connect to tcp server error: %v", err) return } defer c.Close() - return SendTcpMsgByConn(c, msg) + return SendTCPMsgByConn(c, msg) } -func SendTcpMsgByConn(c net.Conn, msg string) (res string, err error) { +func SendTCPMsgByConn(c net.Conn, msg string) (res string, err error) { timer := time.Now().Add(5 * time.Second) c.SetDeadline(timer) c.Write([]byte(msg)) @@ -125,7 +125,7 @@ func SendTcpMsgByConn(c net.Conn, msg string) (res string, err error) { return string(buf[:n]), nil } -func SendUdpMsg(addr string, msg string) (res string, err error) { +func SendUDPMsg(addr string, msg string) (res string, err error) { udpAddr, errRet := net.ResolveUDPAddr("udp", addr) if errRet != nil { err = fmt.Errorf("resolve udp addr error: %v", err) @@ -143,6 +143,7 @@ func SendUdpMsg(addr string, msg string) (res string, err error) { return } + conn.SetReadDeadline(time.Now().Add(10 * time.Second)) buf := make([]byte, 2048) n, errRet := conn.Read(buf) if errRet != nil { @@ -152,7 +153,7 @@ func SendUdpMsg(addr string, msg string) (res string, err error) { return string(buf[:n]), nil } -func SendHttpMsg(method, urlStr string, host string, headers map[string]string, proxy string) (code int, body string, header http.Header, err error) { +func SendHTTPMsg(method, urlStr string, host string, headers map[string]string, proxy string) (code int, body string, header http.Header, err error) { req, errRet := http.NewRequest(method, urlStr, nil) if errRet != nil { err = errRet diff --git a/utils/metric/counter.go b/utils/metric/counter.go index 4b9c7a66..ae16b675 100644 --- a/utils/metric/counter.go +++ b/utils/metric/counter.go @@ -19,9 +19,9 @@ import ( ) type Counter interface { - Count() int64 - Inc(int64) - Dec(int64) + Count() int32 + Inc(int32) + Dec(int32) Snapshot() Counter Clear() } @@ -33,28 +33,28 @@ func NewCounter() Counter { } type StandardCounter struct { - count int64 + count int32 } -func (c *StandardCounter) Count() int64 { - return atomic.LoadInt64(&c.count) +func (c *StandardCounter) Count() int32 { + return atomic.LoadInt32(&c.count) } -func (c *StandardCounter) Inc(count int64) { - atomic.AddInt64(&c.count, count) +func (c *StandardCounter) Inc(count int32) { + atomic.AddInt32(&c.count, count) } -func (c *StandardCounter) Dec(count int64) { - atomic.AddInt64(&c.count, -count) +func (c *StandardCounter) Dec(count int32) { + atomic.AddInt32(&c.count, -count) } func (c *StandardCounter) Snapshot() Counter { tmp := &StandardCounter{ - count: atomic.LoadInt64(&c.count), + count: atomic.LoadInt32(&c.count), } return tmp } func (c *StandardCounter) Clear() { - atomic.StoreInt64(&c.count, 0) + atomic.StoreInt32(&c.count, 0) } diff --git a/utils/net/conn.go b/utils/net/conn.go index 6b1d3fa2..4a122b03 100644 --- a/utils/net/conn.go +++ b/utils/net/conn.go @@ -58,7 +58,7 @@ type ContextConn struct { ctx context.Context } -func NewContextConn(c net.Conn, ctx context.Context) *ContextConn { +func NewContextConn(ctx context.Context, c net.Conn) *ContextConn { return &ContextConn{ Conn: c, ctx: ctx, @@ -227,8 +227,8 @@ func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net. } } -func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c net.Conn, err error) { - c, err = ConnectServerByProxy(proxyUrl, protocol, addr) +func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string, tlsConfig *tls.Config) (c net.Conn, err error) { + c, err = ConnectServerByProxy(proxyURL, protocol, addr) if err != nil { return } diff --git a/utils/net/http.go b/utils/net/http.go index f5df84d3..fa1c34af 100644 --- a/utils/net/http.go +++ b/utils/net/http.go @@ -21,21 +21,21 @@ import ( "strings" ) -type HttpAuthWraper struct { +type HTTPAuthWraper struct { h http.Handler user string passwd string } -func NewHttpBasicAuthWraper(h http.Handler, user, passwd string) http.Handler { - return &HttpAuthWraper{ +func NewHTTPBasicAuthWraper(h http.Handler, user, passwd string) http.Handler { + return &HTTPAuthWraper{ h: h, user: user, passwd: passwd, } } -func (aw *HttpAuthWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (aw *HTTPAuthWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { user, passwd, hasAuth := r.BasicAuth() if (aw.user == "" && aw.passwd == "") || (hasAuth && user == aw.user && passwd == aw.passwd) { aw.h.ServeHTTP(w, r) @@ -45,19 +45,19 @@ func (aw *HttpAuthWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -type HttpAuthMiddleware struct { +type HTTPAuthMiddleware struct { user string passwd string } -func NewHttpAuthMiddleware(user, passwd string) *HttpAuthMiddleware { - return &HttpAuthMiddleware{ +func NewHTTPAuthMiddleware(user, passwd string) *HTTPAuthMiddleware { + return &HTTPAuthMiddleware{ user: user, passwd: passwd, } } -func (authMid *HttpAuthMiddleware) Middleware(next http.Handler) http.Handler { +func (authMid *HTTPAuthMiddleware) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reqUser, reqPasswd, hasAuth := r.BasicAuth() if (authMid.user == "" && authMid.passwd == "") || @@ -70,7 +70,7 @@ func (authMid *HttpAuthMiddleware) Middleware(next http.Handler) http.Handler { }) } -func HttpBasicAuth(h http.HandlerFunc, user, passwd string) http.HandlerFunc { +func HTTPBasicAuth(h http.HandlerFunc, user, passwd string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { reqUser, reqPasswd, hasAuth := r.BasicAuth() if (user == "" && passwd == "") || @@ -83,11 +83,11 @@ func HttpBasicAuth(h http.HandlerFunc, user, passwd string) http.HandlerFunc { } } -type HttpGzipWraper struct { +type HTTPGzipWraper struct { h http.Handler } -func (gw *HttpGzipWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (gw *HTTPGzipWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { gw.h.ServeHTTP(w, r) return @@ -99,8 +99,8 @@ func (gw *HttpGzipWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) { gw.h.ServeHTTP(gzr, r) } -func MakeHttpGzipHandler(h http.Handler) http.Handler { - return &HttpGzipWraper{ +func MakeHTTPGzipHandler(h http.Handler) http.Handler { + return &HTTPGzipWraper{ h: h, } } diff --git a/utils/net/kcp.go b/utils/net/kcp.go index 39eb8987..2788b5f8 100644 --- a/utils/net/kcp.go +++ b/utils/net/kcp.go @@ -21,13 +21,13 @@ import ( kcp "github.com/fatedier/kcp-go" ) -type KcpListener struct { +type KCPListener struct { listener net.Listener acceptCh chan net.Conn closeFlag bool } -func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) { +func ListenKcp(bindAddr string, bindPort int) (l *KCPListener, err error) { listener, err := kcp.ListenWithOptions(fmt.Sprintf("%s:%d", bindAddr, bindPort), nil, 10, 3) if err != nil { return l, err @@ -35,7 +35,7 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) { listener.SetReadBuffer(4194304) listener.SetWriteBuffer(4194304) - l = &KcpListener{ + l = &KCPListener{ listener: listener, acceptCh: make(chan net.Conn), closeFlag: false, @@ -64,7 +64,7 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) { return l, err } -func (l *KcpListener) Accept() (net.Conn, error) { +func (l *KCPListener) Accept() (net.Conn, error) { conn, ok := <-l.acceptCh if !ok { return conn, fmt.Errorf("channel for kcp listener closed") @@ -72,7 +72,7 @@ func (l *KcpListener) Accept() (net.Conn, error) { return conn, nil } -func (l *KcpListener) Close() error { +func (l *KCPListener) Close() error { if !l.closeFlag { l.closeFlag = true l.listener.Close() @@ -80,11 +80,11 @@ func (l *KcpListener) Close() error { return nil } -func (l *KcpListener) Addr() net.Addr { +func (l *KCPListener) Addr() net.Addr { return l.listener.Addr() } -func NewKcpConnFromUdp(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) { +func NewKCPConnFromUDP(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) { kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn) if err != nil { return nil, err diff --git a/utils/net/tls.go b/utils/net/tls.go index d3271226..52a17787 100644 --- a/utils/net/tls.go +++ b/utils/net/tls.go @@ -24,11 +24,11 @@ import ( ) var ( - FRP_TLS_HEAD_BYTE = 0x17 + FRPTLSHeadByte = 0x17 ) func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) { - c.Write([]byte{byte(FRP_TLS_HEAD_BYTE)}) + c.Write([]byte{byte(FRPTLSHeadByte)}) out = tls.Client(c, tlsConfig) return } @@ -44,7 +44,7 @@ func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, t return } - if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE { + if n == 1 && int(buf[0]) == FRPTLSHeadByte { out = tls.Server(c, tlsConfig) } else { if tlsOnly { diff --git a/utils/net/udp.go b/utils/net/udp.go index 28a68139..67d66665 100644 --- a/utils/net/udp.go +++ b/utils/net/udp.go @@ -24,14 +24,14 @@ import ( "github.com/fatedier/golib/pool" ) -type UdpPacket struct { +type UDPPacket struct { Buf []byte LocalAddr net.Addr RemoteAddr net.Addr } -type FakeUdpConn struct { - l *UdpListener +type FakeUDPConn struct { + l *UDPListener localAddr net.Addr remoteAddr net.Addr @@ -42,8 +42,8 @@ type FakeUdpConn struct { mu sync.RWMutex } -func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn { - fc := &FakeUdpConn{ +func NewFakeUDPConn(l *UDPListener, laddr, raddr net.Addr) *FakeUDPConn { + fc := &FakeUDPConn{ l: l, localAddr: laddr, remoteAddr: raddr, @@ -65,7 +65,7 @@ func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn { return fc } -func (c *FakeUdpConn) putPacket(content []byte) { +func (c *FakeUDPConn) putPacket(content []byte) { defer func() { if err := recover(); err != nil { } @@ -77,7 +77,7 @@ func (c *FakeUdpConn) putPacket(content []byte) { } } -func (c *FakeUdpConn) Read(b []byte) (n int, err error) { +func (c *FakeUDPConn) Read(b []byte) (n int, err error) { content, ok := <-c.packets if !ok { return 0, io.EOF @@ -95,7 +95,7 @@ func (c *FakeUdpConn) Read(b []byte) (n int, err error) { return n, nil } -func (c *FakeUdpConn) Write(b []byte) (n int, err error) { +func (c *FakeUDPConn) Write(b []byte) (n int, err error) { c.mu.RLock() if c.closeFlag { c.mu.RUnlock() @@ -103,12 +103,12 @@ func (c *FakeUdpConn) Write(b []byte) (n int, err error) { } c.mu.RUnlock() - packet := &UdpPacket{ + packet := &UDPPacket{ Buf: b, LocalAddr: c.localAddr, RemoteAddr: c.remoteAddr, } - c.l.writeUdpPacket(packet) + c.l.writeUDPPacket(packet) c.mu.Lock() c.lastActive = time.Now() @@ -116,7 +116,7 @@ func (c *FakeUdpConn) Write(b []byte) (n int, err error) { return len(b), nil } -func (c *FakeUdpConn) Close() error { +func (c *FakeUDPConn) Close() error { c.mu.Lock() defer c.mu.Unlock() if !c.closeFlag { @@ -126,54 +126,54 @@ func (c *FakeUdpConn) Close() error { return nil } -func (c *FakeUdpConn) IsClosed() bool { +func (c *FakeUDPConn) IsClosed() bool { c.mu.RLock() defer c.mu.RUnlock() return c.closeFlag } -func (c *FakeUdpConn) LocalAddr() net.Addr { +func (c *FakeUDPConn) LocalAddr() net.Addr { return c.localAddr } -func (c *FakeUdpConn) RemoteAddr() net.Addr { +func (c *FakeUDPConn) RemoteAddr() net.Addr { return c.remoteAddr } -func (c *FakeUdpConn) SetDeadline(t time.Time) error { +func (c *FakeUDPConn) SetDeadline(t time.Time) error { return nil } -func (c *FakeUdpConn) SetReadDeadline(t time.Time) error { +func (c *FakeUDPConn) SetReadDeadline(t time.Time) error { return nil } -func (c *FakeUdpConn) SetWriteDeadline(t time.Time) error { +func (c *FakeUDPConn) SetWriteDeadline(t time.Time) error { return nil } -type UdpListener struct { +type UDPListener struct { addr net.Addr acceptCh chan net.Conn - writeCh chan *UdpPacket + writeCh chan *UDPPacket readConn net.Conn closeFlag bool - fakeConns map[string]*FakeUdpConn + fakeConns map[string]*FakeUDPConn } -func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) { +func ListenUDP(bindAddr string, bindPort int) (l *UDPListener, err error) { udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort)) if err != nil { return l, err } readConn, err := net.ListenUDP("udp", udpAddr) - l = &UdpListener{ + l = &UDPListener{ addr: udpAddr, acceptCh: make(chan net.Conn), - writeCh: make(chan *UdpPacket, 1000), - fakeConns: make(map[string]*FakeUdpConn), + writeCh: make(chan *UDPPacket, 1000), + fakeConns: make(map[string]*FakeUDPConn), } // for reading @@ -189,7 +189,7 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) { fakeConn, exist := l.fakeConns[remoteAddr.String()] if !exist || fakeConn.IsClosed() { - fakeConn = NewFakeUdpConn(l, l.Addr(), remoteAddr) + fakeConn = NewFakeUDPConn(l, l.Addr(), remoteAddr) l.fakeConns[remoteAddr.String()] = fakeConn } fakeConn.putPacket(buf[:n]) @@ -215,7 +215,7 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) { return } -func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) { +func (l *UDPListener) writeUDPPacket(packet *UDPPacket) (err error) { defer func() { if errRet := recover(); errRet != nil { err = fmt.Errorf("udp write closed listener") @@ -225,17 +225,17 @@ func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) { return } -func (l *UdpListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error) { +func (l *UDPListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error) { // only set remote addr here - packet := &UdpPacket{ + packet := &UDPPacket{ Buf: buf, RemoteAddr: remoteAddr, } - err = l.writeUdpPacket(packet) + err = l.writeUDPPacket(packet) return } -func (l *UdpListener) Accept() (net.Conn, error) { +func (l *UDPListener) Accept() (net.Conn, error) { conn, ok := <-l.acceptCh if !ok { return conn, fmt.Errorf("channel for udp listener closed") @@ -243,14 +243,16 @@ func (l *UdpListener) Accept() (net.Conn, error) { return conn, nil } -func (l *UdpListener) Close() error { +func (l *UDPListener) Close() error { if !l.closeFlag { l.closeFlag = true - l.readConn.Close() + if l.readConn != nil { + l.readConn.Close() + } } return nil } -func (l *UdpListener) Addr() net.Addr { +func (l *UDPListener) Addr() net.Addr { return l.addr } diff --git a/utils/tcpmux/httpconnect.go b/utils/tcpmux/httpconnect.go index af0a39f9..145d38a7 100644 --- a/utils/tcpmux/httpconnect.go +++ b/utils/tcpmux/httpconnect.go @@ -26,16 +26,16 @@ import ( "github.com/fatedier/frp/utils/vhost" ) -type HttpConnectTcpMuxer struct { - *vhost.VhostMuxer +type HTTPConnectTCPMuxer struct { + *vhost.Muxer } -func NewHttpConnectTcpMuxer(listener net.Listener, timeout time.Duration) (*HttpConnectTcpMuxer, error) { - mux, err := vhost.NewVhostMuxer(listener, getHostFromHttpConnect, nil, sendHttpOk, nil, timeout) - return &HttpConnectTcpMuxer{mux}, err +func NewHTTPConnectTCPMuxer(listener net.Listener, timeout time.Duration) (*HTTPConnectTCPMuxer, error) { + mux, err := vhost.NewMuxer(listener, getHostFromHTTPConnect, nil, sendHTTPOk, nil, timeout) + return &HTTPConnectTCPMuxer{mux}, err } -func readHttpConnectRequest(rd io.Reader) (host string, err error) { +func readHTTPConnectRequest(rd io.Reader) (host string, err error) { bufioReader := bufio.NewReader(rd) req, err := http.ReadRequest(bufioReader) @@ -52,13 +52,13 @@ func readHttpConnectRequest(rd io.Reader) (host string, err error) { return } -func sendHttpOk(c net.Conn) error { +func sendHTTPOk(c net.Conn) error { return util.OkResponse().Write(c) } -func getHostFromHttpConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) { +func getHostFromHTTPConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) { reqInfoMap := make(map[string]string, 0) - host, err := readHttpConnectRequest(c) + host, err := readHTTPConnectRequest(c) if err != nil { return nil, reqInfoMap, err } diff --git a/utils/util/util.go b/utils/util/util.go index 5b30d543..50069ea4 100644 --- a/utils/util/util.go +++ b/utils/util/util.go @@ -23,13 +23,13 @@ import ( "strings" ) -// RandId return a rand string used in frp. -func RandId() (id string, err error) { - return RandIdWithLen(8) +// RandID return a rand string used in frp. +func RandID() (id string, err error) { + return RandIDWithLen(8) } -// RandIdWithLen return a rand string with idLen length. -func RandIdWithLen(idLen int) (id string, err error) { +// RandIDWithLen return a rand string with idLen length. +func RandIDWithLen(idLen int) (id string, err error) { b := make([]byte, idLen) _, err = rand.Read(b) if err != nil { @@ -105,7 +105,6 @@ func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) { func GenerateResponseErrorString(summary string, err error, detailed bool) string { if detailed { return err.Error() - } else { - return summary } + return summary } diff --git a/utils/util/util_test.go b/utils/util/util_test.go index a7518f6f..311732b4 100644 --- a/utils/util/util_test.go +++ b/utils/util/util_test.go @@ -8,7 +8,7 @@ import ( func TestRandId(t *testing.T) { assert := assert.New(t) - id, err := RandId() + id, err := RandID() assert.NoError(err) t.Log(id) assert.Equal(16, len(id)) diff --git a/utils/vhost/http.go b/utils/vhost/http.go index 1bf6cc30..31759647 100644 --- a/utils/vhost/http.go +++ b/utils/vhost/http.go @@ -35,30 +35,30 @@ var ( ErrNoDomain = errors.New("no such domain") ) -type HttpReverseProxyOptions struct { +type HTTPReverseProxyOptions struct { ResponseHeaderTimeoutS int64 } -type HttpReverseProxy struct { +type HTTPReverseProxy struct { proxy *ReverseProxy - vhostRouter *VhostRouters + vhostRouter *Routers responseHeaderTimeout time.Duration } -func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRouters) *HttpReverseProxy { +func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *HTTPReverseProxy { if option.ResponseHeaderTimeoutS <= 0 { option.ResponseHeaderTimeoutS = 60 } - rp := &HttpReverseProxy{ + rp := &HTTPReverseProxy{ responseHeaderTimeout: time.Duration(option.ResponseHeaderTimeoutS) * time.Second, vhostRouter: vhostRouter, } proxy := &ReverseProxy{ Director: func(req *http.Request) { req.URL.Scheme = "http" - url := req.Context().Value("url").(string) - oldHost := util.GetHostFromAddr(req.Context().Value("host").(string)) + url := req.Context().Value(RouteInfoURL).(string) + oldHost := util.GetHostFromAddr(req.Context().Value(RouteInfoHost).(string)) host := rp.GetRealHost(oldHost, url) if host != "" { req.Host = host @@ -74,9 +74,9 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute ResponseHeaderTimeout: rp.responseHeaderTimeout, DisableKeepAlives: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - url := ctx.Value("url").(string) - host := util.GetHostFromAddr(ctx.Value("host").(string)) - remote := ctx.Value("remote").(string) + url := ctx.Value(RouteInfoURL).(string) + host := util.GetHostFromAddr(ctx.Value(RouteInfoHost).(string)) + remote := ctx.Value(RouteInfoRemote).(string) return rp.CreateConnection(host, url, remote) }, }, @@ -94,7 +94,7 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute // Register register the route config to reverse proxy // reverse proxy will use CreateConnFn from routeCfg to create a connection to the remote service -func (rp *HttpReverseProxy) Register(routeCfg VhostRouteConfig) error { +func (rp *HTTPReverseProxy) Register(routeCfg RouteConfig) error { err := rp.vhostRouter.Add(routeCfg.Domain, routeCfg.Location, &routeCfg) if err != nil { return err @@ -103,31 +103,31 @@ func (rp *HttpReverseProxy) Register(routeCfg VhostRouteConfig) error { } // UnRegister unregister route config by domain and location -func (rp *HttpReverseProxy) UnRegister(domain string, location string) { +func (rp *HTTPReverseProxy) UnRegister(domain string, location string) { rp.vhostRouter.Del(domain, location) } -func (rp *HttpReverseProxy) GetRealHost(domain string, location string) (host string) { +func (rp *HTTPReverseProxy) GetRealHost(domain string, location string) (host string) { vr, ok := rp.getVhost(domain, location) if ok { - host = vr.payload.(*VhostRouteConfig).RewriteHost + host = vr.payload.(*RouteConfig).RewriteHost } return } -func (rp *HttpReverseProxy) GetHeaders(domain string, location string) (headers map[string]string) { +func (rp *HTTPReverseProxy) GetHeaders(domain string, location string) (headers map[string]string) { vr, ok := rp.getVhost(domain, location) if ok { - headers = vr.payload.(*VhostRouteConfig).Headers + headers = vr.payload.(*RouteConfig).Headers } return } // CreateConnection create a new connection by route config -func (rp *HttpReverseProxy) CreateConnection(domain string, location string, remoteAddr string) (net.Conn, error) { +func (rp *HTTPReverseProxy) CreateConnection(domain string, location string, remoteAddr string) (net.Conn, error) { vr, ok := rp.getVhost(domain, location) if ok { - fn := vr.payload.(*VhostRouteConfig).CreateConnFn + fn := vr.payload.(*RouteConfig).CreateConnFn if fn != nil { return fn(remoteAddr) } @@ -135,11 +135,11 @@ func (rp *HttpReverseProxy) CreateConnection(domain string, location string, rem return nil, fmt.Errorf("%v: %s %s", ErrNoDomain, domain, location) } -func (rp *HttpReverseProxy) CheckAuth(domain, location, user, passwd string) bool { +func (rp *HTTPReverseProxy) CheckAuth(domain, location, user, passwd string) bool { vr, ok := rp.getVhost(domain, location) if ok { - checkUser := vr.payload.(*VhostRouteConfig).Username - checkPasswd := vr.payload.(*VhostRouteConfig).Password + checkUser := vr.payload.(*RouteConfig).Username + checkPasswd := vr.payload.(*RouteConfig).Password if (checkUser != "" || checkPasswd != "") && (checkUser != user || checkPasswd != passwd) { return false } @@ -148,7 +148,7 @@ func (rp *HttpReverseProxy) CheckAuth(domain, location, user, passwd string) boo } // getVhost get vhost router by domain and location -func (rp *HttpReverseProxy) getVhost(domain string, location string) (vr *VhostRouter, ok bool) { +func (rp *HTTPReverseProxy) getVhost(domain string, location string) (vr *Router, ok bool) { // first we check the full hostname // if not exist, then check the wildcard_domain such as *.example.com vr, ok = rp.vhostRouter.Get(domain, location) @@ -176,7 +176,7 @@ func (rp *HttpReverseProxy) getVhost(domain string, location string) (vr *VhostR } } -func (rp *HttpReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { +func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { domain := util.GetHostFromAddr(req.Host) location := req.URL.Path user, passwd, _ := req.BasicAuth() diff --git a/utils/vhost/https.go b/utils/vhost/https.go index 41bd01ce..a2b980b3 100644 --- a/utils/vhost/https.go +++ b/utils/vhost/https.go @@ -43,13 +43,13 @@ const ( extensionRenegotiationInfo uint16 = 0xff01 ) -type HttpsMuxer struct { - *VhostMuxer +type HTTPSMuxer struct { + *Muxer } -func NewHttpsMuxer(listener net.Listener, timeout time.Duration) (*HttpsMuxer, error) { - mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, nil, timeout) - return &HttpsMuxer{mux}, err +func NewHTTPSMuxer(listener net.Listener, timeout time.Duration) (*HTTPSMuxer, error) { + mux, err := NewMuxer(listener, GetHTTPSHostname, nil, nil, nil, timeout) + return &HTTPSMuxer{mux}, err } func readHandshake(rd io.Reader) (host string, err error) { @@ -65,9 +65,8 @@ func readHandshake(rd io.Reader) (host string, err error) { length, err := rd.Read(data[47:]) if err != nil { return - } else { - length += 47 } + length += 47 data = data[:length] if uint8(data[5]) != typeClientHello { err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5])) @@ -75,12 +74,12 @@ func readHandshake(rd io.Reader) (host string, err error) { } // session - sessionIdLen := int(data[43]) - if sessionIdLen > 32 || len(data) < 44+sessionIdLen { - err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIdLen) + sessionIDLen := int(data[43]) + if sessionIDLen > 32 || len(data) < 44+sessionIDLen { + err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIDLen) return } - data = data[44+sessionIdLen:] + data = data[44+sessionIDLen:] if len(data) < 2 { err = fmt.Errorf("readHandshake: dataLen[%d] after session is short", len(data)) return @@ -177,11 +176,11 @@ func readHandshake(rd io.Reader) (host string, err error) { } data = data[length:] } - err = fmt.Errorf("Unknow error") + err = fmt.Errorf("Unknown error") return } -func GetHttpsHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) { +func GetHTTPSHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) { reqInfoMap := make(map[string]string, 0) sc, rd := gnet.NewSharedConn(c) host, err := readHandshake(rd) diff --git a/utils/vhost/reverseproxy.go b/utils/vhost/reverseproxy.go index f606f482..571d63a4 100644 --- a/utils/vhost/reverseproxy.go +++ b/utils/vhost/reverseproxy.go @@ -202,9 +202,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // ============================= // Modified for frp - outreq = outreq.WithContext(context.WithValue(outreq.Context(), "url", req.URL.Path)) - outreq = outreq.WithContext(context.WithValue(outreq.Context(), "host", req.Host)) - outreq = outreq.WithContext(context.WithValue(outreq.Context(), "remote", req.RemoteAddr)) + outreq = outreq.WithContext(context.WithValue(outreq.Context(), RouteInfoURL, req.URL.Path)) + outreq = outreq.WithContext(context.WithValue(outreq.Context(), RouteInfoHost, req.Host)) + outreq = outreq.WithContext(context.WithValue(outreq.Context(), RouteInfoRemote, req.RemoteAddr)) // ============================= p.Director(outreq) diff --git a/utils/vhost/router.go b/utils/vhost/router.go index bfdcb50b..dc6a66a3 100644 --- a/utils/vhost/router.go +++ b/utils/vhost/router.go @@ -11,25 +11,25 @@ var ( ErrRouterConfigConflict = errors.New("router config conflict") ) -type VhostRouters struct { - RouterByDomain map[string][]*VhostRouter +type Routers struct { + RouterByDomain map[string][]*Router mutex sync.RWMutex } -type VhostRouter struct { +type Router struct { domain string location string payload interface{} } -func NewVhostRouters() *VhostRouters { - return &VhostRouters{ - RouterByDomain: make(map[string][]*VhostRouter), +func NewRouters() *Routers { + return &Routers{ + RouterByDomain: make(map[string][]*Router), } } -func (r *VhostRouters) Add(domain, location string, payload interface{}) error { +func (r *Routers) Add(domain, location string, payload interface{}) error { r.mutex.Lock() defer r.mutex.Unlock() @@ -39,10 +39,10 @@ func (r *VhostRouters) Add(domain, location string, payload interface{}) error { vrs, found := r.RouterByDomain[domain] if !found { - vrs = make([]*VhostRouter, 0, 1) + vrs = make([]*Router, 0, 1) } - vr := &VhostRouter{ + vr := &Router{ domain: domain, location: location, payload: payload, @@ -54,7 +54,7 @@ func (r *VhostRouters) Add(domain, location string, payload interface{}) error { return nil } -func (r *VhostRouters) Del(domain, location string) { +func (r *Routers) Del(domain, location string) { r.mutex.Lock() defer r.mutex.Unlock() @@ -62,7 +62,7 @@ func (r *VhostRouters) Del(domain, location string) { if !found { return } - newVrs := make([]*VhostRouter, 0) + newVrs := make([]*Router, 0) for _, vr := range vrs { if vr.location != location { newVrs = append(newVrs, vr) @@ -71,7 +71,7 @@ func (r *VhostRouters) Del(domain, location string) { r.RouterByDomain[domain] = newVrs } -func (r *VhostRouters) Get(host, path string) (vr *VhostRouter, exist bool) { +func (r *Routers) Get(host, path string) (vr *Router, exist bool) { r.mutex.RLock() defer r.mutex.RUnlock() @@ -90,7 +90,7 @@ func (r *VhostRouters) Get(host, path string) (vr *VhostRouter, exist bool) { return } -func (r *VhostRouters) exist(host, path string) (vr *VhostRouter, exist bool) { +func (r *Routers) exist(host, path string) (vr *Router, exist bool) { vrs, found := r.RouterByDomain[host] if !found { return @@ -106,7 +106,7 @@ func (r *VhostRouters) exist(host, path string) (vr *VhostRouter, exist bool) { } // sort by location -type ByLocation []*VhostRouter +type ByLocation []*Router func (a ByLocation) Len() int { return len(a) diff --git a/utils/vhost/vhost.go b/utils/vhost/vhost.go index fec3f525..363d0ea9 100644 --- a/utils/vhost/vhost.go +++ b/utils/vhost/vhost.go @@ -26,30 +26,38 @@ import ( "github.com/fatedier/golib/errors" ) +type RouteInfo string + +const ( + RouteInfoURL RouteInfo = "url" + RouteInfoHost RouteInfo = "host" + RouteInfoRemote RouteInfo = "remote" +) + type muxFunc func(net.Conn) (net.Conn, map[string]string, error) type httpAuthFunc func(net.Conn, string, string, string) (bool, error) type hostRewriteFunc func(net.Conn, string) (net.Conn, error) type successFunc func(net.Conn) error -type VhostMuxer struct { +type Muxer struct { listener net.Listener timeout time.Duration vhostFunc muxFunc authFunc httpAuthFunc successFunc successFunc rewriteFunc hostRewriteFunc - registryRouter *VhostRouters + registryRouter *Routers } -func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, successFunc successFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) { - mux = &VhostMuxer{ +func NewMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, successFunc successFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *Muxer, err error) { + mux = &Muxer{ listener: listener, timeout: timeout, vhostFunc: vhostFunc, authFunc: authFunc, successFunc: successFunc, rewriteFunc: rewriteFunc, - registryRouter: NewVhostRouters(), + registryRouter: NewRouters(), } go mux.run() return mux, nil @@ -57,8 +65,8 @@ func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFu type CreateConnFunc func(remoteAddr string) (net.Conn, error) -// VhostRouteConfig is the params used to match HTTP requests -type VhostRouteConfig struct { +// RouteConfig is the params used to match HTTP requests +type RouteConfig struct { Domain string Location string RewriteHost string @@ -71,7 +79,7 @@ type VhostRouteConfig struct { // listen for a new domain name, if rewriteHost is not empty and rewriteFunc is not nil // then rewrite the host header to rewriteHost -func (v *VhostMuxer) Listen(ctx context.Context, cfg *VhostRouteConfig) (l *Listener, err error) { +func (v *Muxer) Listen(ctx context.Context, cfg *RouteConfig) (l *Listener, err error) { l = &Listener{ name: cfg.Domain, location: cfg.Location, @@ -89,7 +97,7 @@ func (v *VhostMuxer) Listen(ctx context.Context, cfg *VhostRouteConfig) (l *List return l, nil } -func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) { +func (v *Muxer) getListener(name, path string) (l *Listener, exist bool) { // first we check the full hostname // if not exist, then check the wildcard_domain such as *.example.com vr, found := v.registryRouter.Get(name, path) @@ -118,7 +126,7 @@ func (v *VhostMuxer) getListener(name, path string) (l *Listener, exist bool) { } } -func (v *VhostMuxer) run() { +func (v *Muxer) run() { for { conn, err := v.listener.Accept() if err != nil { @@ -128,7 +136,7 @@ func (v *VhostMuxer) run() { } } -func (v *VhostMuxer) handle(c net.Conn) { +func (v *Muxer) handle(c net.Conn) { if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil { c.Close() return @@ -195,7 +203,7 @@ type Listener struct { rewriteHost string userName string passWord string - mux *VhostMuxer // for closing VhostMuxer + mux *Muxer // for closing Muxer accept chan net.Conn ctx context.Context } @@ -219,7 +227,7 @@ func (l *Listener) Accept() (net.Conn, error) { xl.Debug("rewrite host to [%s] success", l.rewriteHost) conn = sConn } - return frpNet.NewContextConn(conn, l.ctx), nil + return frpNet.NewContextConn(l.ctx, conn), nil } func (l *Listener) Close() error {