diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..9d8c751
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://tmoonlight.github.io/100lines/111.html']
diff --git a/.gitignore b/.gitignore
index 629e18f..e601f3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -214,3 +214,4 @@ pip-log.txt
#Mr Developer
.mr.developer.cfg
+/wx/.vscode
diff --git a/README.md b/README.md
index dc83670..704d834 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,283 @@
-
-[](https://dev.azure.com/tmoonlight/NSmartProxy/_build/latest?definitionId=1&branchName=master)
+
-# NSmartProxy
+[](https://github.com/tmoonlight/NSmartProxy/releases)
+[](https://github.com/tmoonlight/NSmartProxy/blob/master/LICENSE)
+[](https://dev.azure.com/tmoonlight/NSmartProxy/_build/latest?definitionId=1&branchName=master)
+
+
+
+中文版 \|
+[English](https://github.com/tmoonlight/NSmartProxy/blob/master/README_EN.md)
+
+NSmartProxy
+===========
#### 什么是NSmartProxy?
-NSmartProxy是一款免费的内网穿透工具。
-## 特点
-1. 跨平台,客户端和服务端均可运行在MacOS,Linux,Windows系统上;
-2. 使用方便,配置简单;
-3. 多端映射,一个NSmart Proxy客户端可以同时映射多种服务。
-4. 支持TCP协议栈下的所有协议(已经经过测试的有FTP、Telnet、SMTP、HTTP/HTTPS、POP3、SMB、VNC、RDP。暂不支持UDP协议,开发中。)
+NSmartProxy是一款免费的内网穿透工具。
+使用中如果有任何问题和建议,可以[点击这里加入Gitter群组](https://gitter.im/tmoonlight/NSmartProxy)或者[点击这里加入QQ群
+(群号:813170640)](//shang.qq.com/wpa/qunwpa?idkey=139dc3d01be5cc7ac3226c022d832b8ddcc4ec4b64d8755cd4f5c669994970c7)我们一起讨论。
-## 运行原理
-NSmartProxy包含两个服务程序:
-* 服务端(NSmartServer):部署在外网,用来接收来自最终使用者和客户端的反向连接,并将它们进行相互转发。
-* 客户端(NSmartClientRouter):部署在内网,用来转发访问内网各种服务的请求以及响应。
-
+目录
+----
+ - [特点](#特点)
+ - [运行原理](#运行原理)
+ - [客户端安装](#客户端安装)
+ - [启动准备](#启动准备)
+ - [使用方法](#使用方法)
+ - [服务端安装](#服务端安装)
+ - [启动准备](#启动准备-1)
+ - [使用方法](#使用方法-1)
+ - [使用案例](#使用案例)
-## 启动准备
-#### Linux/Windows/MacOS
-1. 安装[.NET Core Runtime](https://dotnet.microsoft.com/download)
-2. 下载最新版本的[NSmartProxy](https://github.com/tmoonlight/NSmartProxy/releases)
+特点
+----
+
+1. 跨平台,客户端和服务端均可运行在MacOS,Linux,Windows系统上;
+2. 使用方便,配置简单;
+3. 多端映射,只需安装一个NSmartProxy客户端可映射整个局域网内的多种服务;
+4. 支持TCP协议栈下的所有协议(已经经过测试的有FTP、Telnet、SMTP、HTTP/HTTPS、POP3、SMB、VNC、RDP。),以及相当一部分基于UDP的协议(已经经过测试的有DNS查询、mosh服务)。
+
+运行原理
+--------
+
+NSmartProxy包含两个服务程序:
+* 服务端(NSmartProxy.ServerHost):部署在外网,用来接收来自最终使用者和客户端的反向连接,并将它们进行相互转发。
+* 客户端(NSmartProxyClient):部署在内网,用来转发访问内网各种服务的请求以及响应。
+
+
+客户端安装
+----------
+
+NSmartProxy支持各种基于TCP和UDP服务的端口映射,下面以mstsc,iis,ftp以及mosh服务为例:
+
+### 启动准备
+
+NSmartProxy的客户端被打包成三种发布方式:第一种是跨平台包,需要预先安装[.NET
+Core环境](https://dotnet.microsoft.com/download)。
+第二种是SCD包(包名带"scd"),无需安装.net环境,用户需要根据自己的平台和架构选择相应的压缩包。第三种是Windows窗体版本(包名带"winform"):
+#### Windows
+1. 确保客户端的环境在.NET Framework 4.6.1 以上。
+2. 下载最新的窗体版本https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspclient_winform_v1.2_final4.zip
+
+#### Linux
+
+- 下载最新版本的NSmartProxyClient,以SCD发布下的linux x64系统为例:
+
+
+
+ wget https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspserver_scd_linux_v1.2_final4.zip
+
+#### MacOS
+
+- 下载最新版本的NSmartProxyClient:
+
+
+
+ wget https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspclient_scd_osx_v1.2_final4.zip
+
+#### Docker
+
+- 如果当前机器上已经有了docker运行环境,则无需安装运行时,直接拉取镜像即可运行,如下脚本在Docker
+ CE 17.09下测试通过:
+
+
+
+ sudo docker pull tmoonlight/nspclient
+ sudo docker run --name mynspclient -dit tmoonlight/nspclient
+
+### 使用方法
+
+1. 打开安装目录下的appsettings.json文件,配置服务地址,映射地址和端口(winform版本也兼容这种配置方式,也可直接进入界面配置):
+
+
-## 使用方法
-NSmartProxy支持各种基于TCP服务的端口映射,下面以mstsc,iis,ftp服务为例:
-1. 打开安装目录下的appsettings.json文件,配置服务地址,映射地址和端口:
-```
-{
- "ProviderPort": "19974", //反向连接的端口
- "ProviderConfigPort": "12308", //配置服务的端口
- "ProviderAddress": "2017studio.imwork.net", //配置服务的地址,可以是域名(eg.:domain.com)也可以是ip(eg.:211.5.5.4)
- //"ProviderAddress": "192.168.0.106",
-
- //反向代理客户端,可以配置多个
- "Clients": [
- {
- "IP": "127.0.0.1", //反向代理机器的ip
- "TargetServicePort": "3389" //反向代理服务的端口
- "ConsumerPort":"3389" //外网访问端口,如被占用,则会从20000开始按顺序分配端口
- },
- {
- "IP": "127.0.0.1",
- "TargetServicePort": "80"
- },
{
- "IP": "127.0.0.1",
- "TargetServicePort": "21"
+ "ProviderWebPort": 12309, //服务器端口
+ "ProviderAddress": "2017studio.imwork.net", //服务器地址
+
+ //反向代理客户端列表
+ "Clients": [
+ {//mstsc远程控制服务
+ "IP": "127.0.0.1", //反向代理机器的ip
+ "TargetServicePort": "3389" //反向代理服务的端口
+ "ConsumerPort":"3389" //外网访问端口,如被占用,则会从20000开始按顺序分配端口
+ },
+ {//网站服务
+ "IP": "127.0.0.1",
+ "TargetServicePort": "80"
+ },
+ {//ftp服务
+ "IP": "127.0.0.1",
+ "TargetServicePort": "21",
+ "IsCompress" : true, //表示启动传输压缩
+ "Description": "这是一个ftp协议。" //描述字段,方便用户在服务端界面识别
+ },
+ {//mosh服务
+ "IP": "192.168.0.168", //安装mosh服务的受控端地址
+ "TargetServicePort": "60002",
+ "ConsumerPort": "30002",
+ "Protocol": "UDP" //表示是一个UDP协议,如果不加以配置,则以TCP协议来转发
+ }
+ ]
}
- ]
-}
+
+
2. 运行NSmartProxy客户端
+
+- Linux:
+
+
+
+ sudo unzip nspclient_scd_linux_v1.2.zip
+ cd nspclient_scd_linux_v1.2
+ chmod +x ./NSmartProxyClient
+ ./NSmartProxyClient
+
+- MacOS:
+
+
+
+ sudo unzip nspclient_osx_linux_v1.2.zip
+ cd nspclient_scd_osx_v1.2
+ chmod +x ./NSmartProxyClient
+ ./NSmartProxyClient
+
+- Windows: 解压后运行NSmartProxyWinform.exe即可:
+
+
+
+
+3. 后台运行:
+ 您还可以将NSmartProxy客户端注册为一个后台服务,方法如下:
+
+- Windows:
+ - 方法一
+ 
+
+ - 方法二
```
-
-2. 运行NSmartProxy
+ rem 注册客户端windows服务
+ .\NSmartProxyClient action:install
+```
+```
+ rem 卸载客户端windows服务
+ .\NSmartProxyClient action:uninstall
+```
+- MacOS/Linux 暂略
-* Linux:
+#### 客户端登陆
+默认情况下,客户端以匿名登陆,这种方式会在NSmartProxyServer端创建一个随机匿名用户(前提是服务端配置了允许匿名登陆),如果想显式使用特定用户登陆,需要在第一次运行时增加-u 用户名 -p 密码参数,程序会在当前目录生成一份凭据(.usercache)方便下次自动登陆。
+例如输入以下指令来生成一个用户名admin,密码admin123的凭据:
```
- sudo unzip client.zip
- cd client
- sudo dotnet NSmartProxyClient.dll
+./NSmartProxyClient -u admin -p admin123
```
+下次仅需使用:
+```
+./NSmartProxyClient
+```
+自动采用上次的admin用户登陆,如需恢复匿名登陆,则需要删除当前目录下的.usercache文件。
+
+- P.S:
+ 以上是客户端的配置方法,一般情况下,只要用我的免费服务(2017studio.imwork.net)即可进行内网映射了,如果你还想自己搭建服务端,请接着往下看。
+
+服务端安装
+----------
+
+这里介绍NSmartProxy服务端的安装方法(linux,windows,MacOS均适用)
+
+### 启动准备
+
+- 首先你需要一台具备独立IP的服务器,以下安装过程均在此机器上执行:
+#### Linux/Windows/MacOS
+
+1. NSmartProxy的服务端程序被打包成两种发布方式。第一种是跨平台包,需要预先安装[.NET
+ Core环境](https://dotnet.microsoft.com/download)。
+ 第二种是SCD包(包名带"scd"),无需安装.net环境,用户需要根据自己的平台和架构选择相应的压缩包。
+2. 下载最新版的NSmartProxy服务端:
+- Linux:
+
+ wget https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspserver_scd_linux_v1.2_final4.zip
+
+- Windows:
+下载https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspserver_scd_win_v1.2_final4.zip
+
+- MacOS:
+
+
+ wget https://github.com/tmoonlight/NSmartProxy/releases/download/v1.2_final4/nspserver_scd_osx_v1.2_final4.zip
+
+#### Docker
+
+- 无需安装运行时,直接拉取镜像即可运行,运行镜像时需要4组端口:配置端口,反向连接端口,API服务端口,以及使用端口,如下脚本在Docker
+ CE 17.09下测试通过:
+
+
+
+ sudo docker pull tmoonlight/nspserver
+ sudo docker run --name mynspserver -dit -p 7842:7842 -p 7841:7841 -p 12309:12309 -p 20000-20050 tmoonlight/nspserver
+
+### 使用方法
+
+1. 解压缩NSmartProxy服务端的压缩包,以下以SCD发布下的linux系统为例
+
+
+
+ unzip nspserver_scd_linux_v1.2_final4.zip
+
+2. 打开安装目录下的appsettings.json文件,设置反向连接端口和配置服务端口,如果没有特殊需求,默认就好:
+
+
+
+ {
+ "ReversePort": 7842, //反向连接端口
+ "ConfigPort": 7841, //配置服务端口
+ "WebAPIPort": 12309 //API服务端口
+ }
+
+3. 运行NSmartProxy
+
+第一步 cd到安装目录
第二步 执行以下命令
+* Linux/MacOS:
+
+ chmod +x ./NSmartProxy.ServerHost
+ ./NSmartProxy.ServerHost
+
* Windows:
+点击 Win+R 打开运行窗口. 输入 "cmd" 按下Ctrl+Shift+Enter打开管理员身份运行的命令行窗口。cd到安装目录,运行如下指令:
+
+ NSmartProxy.ServerHost
- 解压client.zip,运行run.cmd即可
+第三步 登陆http://ip:12309 进入web端,出厂用户密码为admin/admin
-* P.S: 以上是客户端的配置方法,一般情况下,只要用我的免费服务(2017studio.imwork.net)即可进行内网映射了,如果您还想自己搭建NSmartProxy服务端,请参考[这里](https://github.com/tmoonlight/NSmartProxy/blob/master/README_SERVER.md)。
+
-## 使用案例
+第四步 进入服务端对用户进行各种管理操作
+
+
+
+#### 注册为后台服务
+NSmartProxy客户端和服务端均可以注册为一个后台服务,方法如下:
+* Windows
+ 以管理员身份打开命令行后,cd到程序运行目录,运行以下指令进行服务的注册和卸载:
+
+
+
+ rem 注册服务端windows服务
+ .\NSmartProxy.ServerHost action:install
+
+ rem 卸载服务端windows服务
+ .\NSmartProxy.ServerHost action:uninstall
+
+* MacOS/Linux
+可参考wiki: [How To: 30秒使用Linux搭建一个内网穿透服务端](https://github.com/tmoonlight/NSmartProxy/wiki/How-To:-30%E7%A7%92%E4%BD%BF%E7%94%A8Linux%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E6%9C%8D%E5%8A%A1%E7%AB%AF)
+
+使用案例
+--------
以上已经讲述了将内网的服务映射到外网的方法,还有更多有趣的用法等着你发掘:
-1.远程开机
-
-2.使用windows远程控制操作办公室电脑
-
-3.告别昂贵的vps,以极低的成本制作一个更强大的服务集群
-4.使用ssh等工具在当事人毫不知情的情况下监控他们的电脑,防止妻子外遇,孩子早恋(比较不推荐)
-...etc
-
+1. 远程开机
+2. [使用windows远程控制操作办公室电脑](https://github.com/tmoonlight/NSmartProxy/wiki/How-To:-%E4%BD%BF%E7%94%A8NSmartProxy%E5%AE%9E%E7%8E%B0windows%E4%B8%8A%E7%9A%84%E8%BF%9C%E7%A8%8B%E5%8A%9E%E5%85%AC)
+3. 告别昂贵的vps,以极低的成本制作一个更强大的服务集群
diff --git a/README_EN.md b/README_EN.md
new file mode 100644
index 0000000..583de92
--- /dev/null
+++ b/README_EN.md
@@ -0,0 +1,90 @@
+
+
+[](https://github.com/tmoonlight/NSmartProxy/releases)
+[](https://github.com/tmoonlight/NSmartProxy/blob/master/LICENSE)
+[](https://dev.azure.com/tmoonlight/NSmartProxy/_build/latest?definitionId=1&branchName=master)
+
+
+[中文版](https://github.com/tmoonlight/NSmartProxy/blob/master/README.md) | English
+
+# NSmartProxy
+
+#### What is NSmartProxy?
+NSmartProxy is a reverse proxy tool that creates a secure tunnel from a public endpoint to a locally service.
+
+## Characteristics
+1. Cross-platform, client and server can run on MacOS, Linux, Windows systems;
+2. Easy to use and simple to configure;
+3. Multi-end mapping, one NSmartProxy client can map multiple service nodes.
+
+4. Supports all protocols under the TCP protocol stack (such as FTP, Telnet, SMTP, HTTP/HTTPS, POP3, SMB, VNC, RDP. UDP protocol is not supported at present.)
+
+## Operating principle
+NSmartProxy contains two service programs:
+* Server (NSPServer): Deployed on the external network to receive reverse connections from users and NSPClients and forward them to each other.
+* Client (NSPClient): Deployed on the internal network to forward requests and responses to access various services on the intranet.
+
+
+## Preparation
+#### Linux/Windows/MacOS
+1. Install [.NET Core Runtime](https://dotnet.microsoft.com/download)
+2. Download the latest version of [NSmartProxy](https://github.com/tmoonlight/NSmartProxy/releases)
+#### Docker
+* You can run the nspserver directly without having to install the runtime:
+```
+sudo docker pull tmoonlight/nspclient
+sudo docker run --name mynspclient -dit tmoonlight/nspclient
+```
+
+## Instructions
+NSmartProxy supports various port mappings based on TCP services. The following is an example of nspclient configuration which contains mstsc, iis, and ftp services:
+1. Open the appsettings.json file in the installation directory, edit the service address, port,and map-rule as follow:
+```
+{
+ "ProviderWebPort": 12309, //Configure the port of the NSPServer service
+ "ProviderAddress": "2017studio.imwork.net", //Configure the address of the NSPServer service
+
+ //NSPClients, you can configure multiple
+ "Clients": [
+ {
+ "IP": "127.0.0.1", //Reverse proxy machine ip
+ "TargetServicePort": "3389" //Port of the reverse proxy service
+ "ConsumerPort":"3389" //External network access port, if occupied,the nspclient will allocate ports in order from 20000
+ },
+ {
+ "IP": "127.0.0.1",
+ "TargetServicePort": "80"
+ },
+ {
+ "IP": "127.0.0.1",
+ "TargetServicePort": "21"
+ }
+ ]
+}
+```
+
+2. Run NSmartProxy
+
+* Linux:
+```
+ sudo unzip client.zip
+ cd client
+ sudo dotnet NSmartProxyClient.dll
+```
+* Windows:
+
+ Unzip nspclient*.zip and run NSmartProxyWinform.exe:
+
+
+* P.S: The above is the configuration method of the client. In general, you can use the free service (2017studio.imwork.net) to perform intranet mapping. If you want to build the NSmartProxy server yourself, please click [here](https://github.com/tmoonlight/NSmartProxy/blob/master/README_SERVER.md).
+
+## Use Cases
+We have already described the method of mapping the services of the intranet to the external network, and there are more interesting usages waiting for you to
+discover:
+1.Remote boot
+
+2.Use windows remote control to operate the office computer
+
+3.Say goodbye to expensive vps and make a more powerful service cluster at a very low cost
+...etc
+
diff --git a/README_SERVER.md b/README_SERVER.md
index 92816de..0eddf19 100644
--- a/README_SERVER.md
+++ b/README_SERVER.md
@@ -1,39 +1,53 @@
-
-# NSmartProxy ServerHost
+# NSmartProxy Server
-这里介绍NSmartProxy服务端的安装方法(linux,windows,MacOS均适用)
+Here is the installation method of NSmartProxy server (Linux, windows, MacOS are compatible)
-## 启动准备
-* 首先你需要一台具备独立IP的服务器,以下安装过程均在此机器上执行:
+## Startup preparation
+* First of all, you need a server with a separate IP, the following installation process is performed on this machine:
#### Linux/Windows/MacOS
-1.安装[.NET Core环境](https://dotnet.microsoft.com/download)
-2.下载最新版的[NSmartProxy](https://github.com/tmoonlight/NSmartProxy/releases)
+1. Install [.NET Core Environment](https://dotnet.microsoft.com/download)
+2. Download the latest version of [NSmartProxy](https://github.com/tmoonlight/NSmartProxy/releases)
+
+#### Docker
+* You can run the nspserver directly without having to install the runtime. Four sets of ports are required to run the docker image: configuration port, reverse connection port, API service port and consumer port:
+```
+sudo docker pull tmoonlight/nspserver
+sudo docker run --name mynspserver -dit -p 7842:7842 -p 7841:7841 -p 12309:12309 -p 20000-20050 tmoonlight/nspserver
+```
-## 使用方法
-1. 解压缩NSmartProxy服务端的压缩包。
-2. 打开安装目录下的appsettings.json文件,设置反向连接端口和配置服务端口:
+## Instructions
+1. Unzip the package of NSmartProxy server.
+2. Open the appsettings.json file in the installation directory, set the reverse connection port and configure the service port:
```
{
- "ClientServicePort": 9974, //反向连接端口
- "ConfigServicePort": 12308 //配置服务端口
+ "ReversePort": 7842, //Reverse connection port
+ "ConfigPort": 7841, //Configure the service port
+ "WebAPIPort": 12309 //API service port
}
```
-3. 运行NSmartProxy
+3. Run NSmartProxy Server
-第一步 cd到安装目录
-第二步 执行以下命令
-* Linux/MacOS:
+
+* Linux/MacOS:
+Change directory to the installation directory ,then execute the following command:
```
sudo dotnet NSmartProxy.ServerHost.dll
```
-* Windows:
-
+* Windows:
+Press Windows+R to open the “Run” box. Type “cmd” into the box and then press Ctrl+Shift+Enter to run the command as an administrator.
+Change directory to the installation directory ,then execute the following command:
```
-运行安装目录下的run.cmd
+dotnet NSmartProxy.ServerHost.dll
```
+In the next step,you can log in to http://youraddress:12309 and enter the web terminal. The default user password is admin/admin.
+
+
+
+And enter the server to perform various management operations.
+
diff --git a/README_SERVER_CN.md b/README_SERVER_CN.md
new file mode 100644
index 0000000..7f4c510
--- /dev/null
+++ b/README_SERVER_CN.md
@@ -0,0 +1,66 @@
+
+
+
+# NSmartProxy ServerHost
+
+这里介绍NSmartProxy服务端的安装方法(linux,windows,MacOS均适用)
+
+## 启动准备
+* 首先你需要一台具备独立IP的服务器,以下安装过程均在此机器上执行:
+#### Linux/Windows/MacOS
+1.安装[.NET Core环境](https://dotnet.microsoft.com/download)
+2.下载最新版的[NSmartProxy](https://github.com/tmoonlight/NSmartProxy/releases
+
+#### Docker
+* 无需安装运行时,直接拉取镜像即可运行,运行镜像时需要4组端口:配置端口,反向连接端口,API服务端口,以及使用端口 :
+```
+sudo docker pull tmoonlight/nspserver
+sudo docker run --name mynspserver -dit -p 7842:7842 -p 7841:7841 -p 12309:12309 -p 20000-20050 tmoonlight/nspserver
+```
+
+## 使用方法
+1. 解压缩NSmartProxy服务端的压缩包。
+2. 打开安装目录下的appsettings.json文件,设置反向连接端口和配置服务端口:
+```
+{
+ "ReversePort": 7842, //反向连接端口
+ "ConfigPort": 7841, //配置服务端口
+ "WebAPIPort": 12309 //API服务端口
+}
+```
+
+3. 运行NSmartProxy
+
+第一步 cd到安装目录
+第二步 执行以下命令
+* Linux/MacOS:
+```
+sudo dotnet NSmartProxy.ServerHost.dll
+```
+* Windows:
+点击 Win+R 打开运行窗口. 输入 “cmd” 按下 Ctrl+Shift+Enter打开管理员身份运行的命令行窗口。 cd到安装目录,运行如下指令:
+
+```
+dotnet NSmartProxy.ServerHost.dll
+```
+
+第三步 登陆http://ip:12309 进入web端,出厂用户密码为admin/admin
+
+
+
+第四步 进入服务端对用户进行各种管理操作
+
+
+
+* 注册为后台服务
+您还可以将NSmartProxy客户端注册为一个后台服务,方法如下:
+以管理员身份打开命令行后,运行以下指令进行服务的注册和卸载:
+```
+rem 注册windows服务
+dotnet NSmartProxy.ServerHost.dll action:install
+```
+
+```
+rem 卸载windows服务
+dotnet NSmartProxy.ServerHost.dll action:uninstall
+```
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..c419263
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 78ac540..6ea3358 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -7,15 +7,19 @@ trigger:
- master
pool:
- vmImage: 'windows-latest'
+ vmImage: 'ubuntu-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
+ clientImageName: 'nspclient'
+ serverImageName: 'nspserver'
+ clientProjectName: 'NSmartProxyClient'
+ serverProjectName: 'NSmartProxy.ServerHost'
steps:
-- task: NuGetToolInstaller@0
+# - task: NuGetToolInstaller@0
# - task: NuGetCommand@2
# inputs:
@@ -24,7 +28,7 @@ steps:
# - task: VSBuild@1
# inputs:
# solution: '$(solution)'
-# msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
+# msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(System.DefaultWorkingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
# platform: '$(buildPlatform)'
# configuration: '$(buildConfiguration)'
@@ -32,12 +36,51 @@ steps:
# inputs:
# platform: '$(buildPlatform)'
# configuration: '$(buildConfiguration)'
+- bash: |
+ mkdir ./build
+ mkdir ./build/$(clientImageName)
+ mkdir ./build/$(clientImageName)/$(clientProjectName)
+ mkdir ./build/nspserver
+ mkdir ./build/nspserver/NSmartProxy.ServerHost
+ displayName: Create build directory
+ workingDirectory: '$(System.DefaultWorkingDirectory)/'
+
- task: DotNetCoreCLI@2
inputs:
- command: 'build'
- projects: './src/NSmartProxy.ClientRouter/NSmartProxy.ClientRouter.csproj'
+ command: 'publish'
+ publishWebProjects: false
+ projects: './src/NSmartProxy.ServerHost/NSmartProxy.ServerHost.csproj'
+ arguments: ' --output $(System.DefaultWorkingDirectory)/build/nspserver /p:PublishTrimmed=false'
+ workingDirectory: '$(System.DefaultWorkingDirectory)/'
+ zipAfterPublish: false
+
- task: DotNetCoreCLI@2
inputs:
- command: 'build'
- projects: './src/NSmartProxy.ServerHost/NSmartProxy.ServerHost.csproj'
+ command: 'publish'
+ publishWebProjects: false
+ projects: './src/NSmartProxyClient/NSmartProxyClient.csproj'
+ arguments: ' --output $(System.DefaultWorkingDirectory)/build/nspclient /p:PublishTrimmed=false'
+ workingDirectory: '$(System.DefaultWorkingDirectory)/'
+ zipAfterPublish: false
+
+
+- task: Bash@3
+ inputs:
+ targetType: 'inline'
+ script: |
+ docker login -u $(dhName) -p $(dhPwd)
+ docker build . -t nspclient
+ docker tag nspclient tmoonlight/nspclient
+ docker push tmoonlight/nspclient
+ workingDirectory: '$(System.DefaultWorkingDirectory)/build/nspclient/NSmartProxyClient'
+
+- task: Bash@3
+ inputs:
+ targetType: 'inline'
+ script: |
+ docker login -u $(dhName) -p $(dhPwd)
+ docker build . -t nspserver
+ docker tag nspserver tmoonlight/nspserver
+ docker push tmoonlight/nspserver
+ workingDirectory: '$(System.DefaultWorkingDirectory)/build/nspserver/$(serverProjectName)'
diff --git a/imgs/servicecn.png b/imgs/servicecn.png
new file mode 100644
index 0000000..d669eb8
Binary files /dev/null and b/imgs/servicecn.png differ
diff --git a/imgs/serviceen.png b/imgs/serviceen.png
new file mode 100644
index 0000000..f3b852a
Binary files /dev/null and b/imgs/serviceen.png differ
diff --git a/imgs/supervisor.png b/imgs/supervisor.png
new file mode 100644
index 0000000..7148b9b
Binary files /dev/null and b/imgs/supervisor.png differ
diff --git a/plugins/NSmartProxyFTP/FtpClient.cs b/plugins/NSmartProxyFTP/FtpClient.cs
new file mode 100644
index 0000000..14d1b92
--- /dev/null
+++ b/plugins/NSmartProxyFTP/FtpClient.cs
@@ -0,0 +1,517 @@
+//作者:Mcdull
+//说明:FTP客户端类,每个客户端封装一个套接字负责接收和返回数据
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net.Sockets;
+using System.Threading;
+using System.Net;
+using System.IO;
+using System.Globalization;
+
+namespace FtpServer
+{
+ class FtpClient
+ {
+ private Socket currentSocket;
+ private Thread thread;
+ private FtpRequest request;
+ private bool isClosed;
+ private Encoding encode;
+ private string rootDir;
+
+ public User user;
+ public event Action Quit;
+ public event Action Login;
+
+ public FtpClient(Socket socket, IPAddress pasv_ip, int pasv_port, IPAddress pasv_proxy_ip, int pasv_proxy_port)
+ {
+ isClosed = false;
+ user = new User();
+ this.request = new FtpRequest(this, pasv_ip, pasv_port, pasv_proxy_ip, pasv_proxy_port);
+ this.currentSocket = socket;
+ encode = Encoding.Default;
+ }
+
+ public IPAddress IP
+ {
+ get { return ((IPEndPoint)currentSocket.RemoteEndPoint).Address; }
+ }
+
+ public void Start()
+ {
+ thread = new Thread(() =>
+ {
+ SendMessage("220 欢迎使用FTP服务器,你已经连上了服务器...");
+ var type = request.Handle(receiveMsg()[0].tokens);
+ if (type == RequestType.OPTS)
+ {
+ if (request.Handle(receiveMsg()[0].tokens) != RequestType.LOGIN_USER)
+ {
+ SendMessage("221 命令错误");
+ close();
+ return;
+ }
+ }
+ else if (type != RequestType.LOGIN_USER)
+ {
+ SendMessage("221 命令错误");
+ close();
+ return;
+ }
+ SendMessage("331 请输入用户 " + user.username + " 的登录密码");
+ if (request.Handle(receiveMsg()[0].tokens) != RequestType.LOGIN_PASS)
+ {
+ SendMessage("221 命令错误");
+ close();
+ return;
+ }
+ var u = FtpServer.Users.SingleOrDefault(p => p.username == user.username && p.password == user.password);
+ if (u != null)
+ {
+ user.isLogin = true;
+ user.workingDir = rootDir = u.rootDir;
+ SendMessage("230 用户 " + user.username + " 授权登录.");
+ onLogin();
+ }
+ else
+ {
+ SendMessage("530 用户名或者密码错误。");
+ close();
+ return;
+ }
+ while (user.isLogin && !isClosed)
+ {
+ if (currentSocket.Connected) // && currentSocket.Available > 0 为了捕获receiveMsg异常从而关闭连接这里注释掉
+ {
+ var tokens = receiveMsg();
+ foreach (var t in tokens)
+ {
+ request.Handle(t.tokens);
+ }
+ }
+ Thread.Sleep(500);
+ }
+ });
+ thread.Start();
+ }
+
+ public void Stop()
+ {
+ close(false);
+ if (thread != null)
+ thread.Abort();
+ }
+
+ #region Request处理各种请求方法
+ //获取发送来的文件
+ public bool ReceiveFile(Socket tempSocket, string filename)
+ {
+ string name = getFileName(filename);
+ FileInfo fi = new FileInfo(name);
+ if (fi.Exists)
+ return false;
+ if (tempSocket != null && tempSocket.Connected)
+ {
+ string dir = Path.GetDirectoryName(name);
+ if (!Directory.Exists(dir))
+ {
+ Directory.CreateDirectory(dir);
+ }
+ byte[] buffer = new byte[1024];
+ using (FileStream fs = new FileStream(name, FileMode.CreateNew, FileAccess.Write, FileShare.Write))
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ int length = 0;
+ do
+ {
+ length = tempSocket.Receive(buffer);
+ fs.Write(buffer, 0, length);
+ }
+ while (length > 0);
+ fs.Close(); //这句话可能是多余的
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //为目录或者文件改名
+ public int Rename(string from, string to)
+ {
+ string name = getFileName(from);
+ string new_name = getFileName(to);
+ Console.WriteLine("重命名" + name + "到" + new_name);
+ FileInfo fi = new FileInfo(name);
+ if (Directory.Exists(name))
+ {
+ Directory.Move(name, new_name);
+ return 1;
+ }
+ else if (fi.Exists)
+ {
+ fi.MoveTo(new_name);
+ return 2;
+ }
+ return 0;
+ }
+
+ //删除文件或目录
+ public int Delete(string dirname)
+ {
+ string dir = getFileName(dirname);
+ Console.WriteLine("删除目录/文件" + dir);
+ FileInfo fi = new FileInfo(dir);
+ if (Directory.Exists(dir))
+ {
+ Directory.Delete(dir, true);
+ return 1;
+ }
+ else if (fi.Exists)
+ {
+ fi.Delete();
+ return 2;
+ }
+ return 0;
+ }
+
+ //跳转到其他目录
+ public bool GotoDir(string targetDir)
+ {
+ string dir = trimEnd(targetDir);
+ if (dir == "..")
+ {
+ //转到上一级目录
+ if (user.workingDir != rootDir)
+ {
+ //如果当前目录不是根目录,就转到上一级目录
+ try
+ {
+ DirectoryInfo di = Directory.GetParent(user.workingDir);
+ if (di != null)
+ user.workingDir = di.FullName;
+ return true;
+ }
+ catch (ArgumentNullException)
+ {
+ Console.WriteLine("路径为空");
+ return false;
+ }
+ catch (ArgumentException)
+ {
+ Console.WriteLine("当前工作路径是一个空字符串,路径中只能包含空格或者其他任何有效的字符。");
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (dir == ".")
+ {
+ return true;
+ }
+ else if (dir[0] == '/')
+ {
+ //是否从根目录开始
+ dir = dir.TrimStart("/".ToCharArray());
+ dir = rootDir + "\\" + dir;
+ try
+ {
+ DirectoryInfo di = new DirectoryInfo(dir);
+ if (di.Exists)
+ {
+ user.workingDir = di.FullName;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (NotSupportedException)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ string workingDirName = new DirectoryInfo(user.workingDir).Name;
+ var temp = dir.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ if (temp.Length > 0 && workingDirName == temp[0])
+ {
+ int i = dir.IndexOf('/');
+ if (i > 0)
+ {
+ dir = dir.Substring(i + 1, dir.Length - i - 1);
+ }
+ }
+ //进入目录
+ dir = user.workingDir + "\\" + dir;
+ try
+ {
+ DirectoryInfo di = new DirectoryInfo(dir);
+ if (di.Exists)
+ {
+ user.workingDir = di.FullName;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (NotSupportedException)
+ {
+ return false;
+ }
+ }
+ }
+
+ //创建目录
+ public bool CreateDir(string dirName)
+ {
+ string dir = getFileName(dirName);
+ Console.WriteLine("创建目录" + dir);
+ if (Directory.Exists(dir))
+ {
+ return false;
+ }
+ else
+ {
+ Directory.CreateDirectory(dir);
+ return true;
+ }
+ }
+
+ //向客户端发送文件
+ public byte[] GetFile(string filename)
+ {
+ string name = getFileName(filename);
+ FileInfo fi = new FileInfo(name);
+ if (fi.Exists)
+ {
+ FileStream fs = fi.OpenRead();
+ byte[] b = new byte[fs.Length];
+ fs.Read(b, 0, b.Length);
+ fs.Close();
+ return b;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ //更新传输编码
+ public void UpdateEncode(string name, string mode)
+ {
+ if (mode.ToUpper() == "ON")
+ {
+ switch (name.ToUpper())
+ {
+ case "UTF8":
+ encode = Encoding.UTF8;
+ break;
+ }
+ }
+ else
+ encode = Encoding.Default;
+ }
+
+ //列表当前目录文件
+ public string GetCurrentDirList()
+ {
+ string[] dirs = Directory.GetDirectories(user.workingDir);
+ string[] files = Directory.GetFiles(user.workingDir);
+ string msg = "";
+ DateTimeFormatInfo dateTimeFormat = new CultureInfo("en-US", true).DateTimeFormat;
+ for (int i = 0; i < files.Length; i++)
+ {
+ FileInfo fi = new FileInfo(files[i]);
+ string name = fi.Name;
+ //msg += string.Format("{0:yyyy-MM-dd HH:mm:ss} {1} {2}\r\n", fi.LastWriteTimeUtc, fi.Length, name);
+ msg += string.Format("-rw-r--r-- 1 root root {0} {1} {2} {3:HH:mm} {4}\r\n", fi.Length, dateTimeFormat.GetMonthName(fi.LastWriteTime.Month).Substring(0, 3),
+ fi.LastWriteTime.Day, fi.LastWriteTime, name);
+ }
+
+ for (int i = 0; i < dirs.Length; i++)
+ {
+ DirectoryInfo di = new DirectoryInfo(dirs[i]);
+ string name = di.Name;
+ //msg += string.Format("{0:yyyy-MM-dd HH:mm:ss} {1} {2}\r\n", di.LastWriteTimeUtc, "", name);
+ msg += string.Format("drw-r--r-- 2 0 0 0 {0} {1} {2:HH:mm} {3}\r\n", dateTimeFormat.GetMonthName(di.LastWriteTime.Month).Substring(0, 3),
+ di.LastWriteTime.Day, di.LastWriteTime, name);
+ }
+ return msg;
+ }
+
+ //获取当前目录
+ public string GetCurrentDir()
+ {
+ string dir = user.workingDir;
+ dir = dir.Remove(0, rootDir.Length);
+ dir = dir.Replace("\\", "/");
+ if (dir == "")
+ dir = "/";
+ else if (dir[0] != '/')
+ dir = "/" + dir;
+ return dir;
+ }
+
+ public void LoginOut()
+ {
+ user.isLogin = false;
+ close();
+ }
+
+ ///
+ /// 当前Socket发送消息
+ ///
+ ///
+ public void SendMessage(string msg)
+ {
+ msg += "\r\n";
+ sendMsg(encode.GetBytes(msg.ToCharArray()));
+ }
+
+ ///
+ /// 根据前一个PORT指定的Socket发送消息
+ ///
+ ///
+ public void SendMessageByTempSocket(Socket tempSocket, string msg)
+ {
+ if (tempSocket != null && tempSocket.Connected)
+ {
+ sendMsg(encode.GetBytes(msg.ToCharArray()), tempSocket);
+ //sendMsg(Encoding.Default.GetBytes(msg.ToCharArray()), tempSocket);
+ tempSocket.Close();
+ }
+ }
+
+ public void SendMessageByTempSocket(Socket tempSocket, byte[] msg)
+ {
+ if (msg.Length > 0)
+ {
+ if (tempSocket != null && tempSocket.Connected)
+ {
+ sendMsg(msg, tempSocket);
+ tempSocket.Close();
+ }
+ }
+ }
+ #endregion
+
+ #region 私有方法
+ private string getFileName(string name)
+ {
+ if (name[0] == '/')
+ {
+ name = name.TrimStart("/".ToCharArray());
+ return rootDir + "\\" + name;
+ }
+ else
+ return user.workingDir + "\\" + trimEnd(name);
+ }
+
+ private string trimEnd(string str)
+ {
+ string dir = "";
+ int pos = str.IndexOf("\r\n");
+ if (pos > -1)
+ {
+ dir = str.Substring(0, pos);
+ }
+ else
+ {
+ dir = str;
+ }
+ return dir;
+ }
+
+ private void sendMsg(Byte[] message, Socket socket = null)
+ {
+ try
+ {
+ if (socket == null)
+ currentSocket.Send(message, message.Length, 0);
+ else
+ socket.Send(message, message.Length, 0);
+ }
+ catch
+ {
+
+ }
+ }
+
+ private List receiveMsg()
+ {
+ List list = new List();
+ byte[] buff = new byte[1024];
+ try
+ {
+ currentSocket.Receive(buff);
+ string clientCommand = encode.GetString(buff);
+ clientCommand = clientCommand.Trim("\0".ToCharArray());
+ clientCommand = clientCommand.Trim("\r\n".ToCharArray());//"PORT 192,168,0,105,51,49\r\nLIST\r\n"这种情况怎么处理,2条命令同时发来
+ var msgs = clientCommand.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ foreach (var msg in msgs)
+ {
+ var token = new Token();
+ if (msg.Length > 0)
+ {
+ var index = msg.IndexOf(" ");
+ if (index > -1)
+ {
+ // token.tokens = msg.Split(new char[] { ' ' });
+ token.tokens = new string[2]{
+ msg.Substring(0, index),
+ msg.Substring(index+1, msg.Length- index-1)
+ };
+
+ }
+ else
+ token.tokens = new string[] { msg };
+ }
+ list.Add(token);
+ }
+ }
+ catch
+ {
+ close();
+ }
+ return list;
+ }
+
+ private void close(bool @event = true)
+ {
+ if (!isClosed)
+ {
+ isClosed = true;
+ currentSocket.Close();
+ request.Dispose();
+ if (@event)
+ {
+ var temp = Quit;
+ if (temp != null)
+ temp(this);
+ }
+ }
+ }
+
+ private void onLogin()
+ {
+ var temp = Login;
+ if (temp != null)
+ temp(this);
+ }
+
+ sealed class Token
+ {
+ internal string[] tokens { get; set; }
+ }
+ #endregion
+ }
+}
diff --git a/plugins/NSmartProxyFTP/FtpRequest.cs b/plugins/NSmartProxyFTP/FtpRequest.cs
new file mode 100644
index 0000000..ebeacd9
--- /dev/null
+++ b/plugins/NSmartProxyFTP/FtpRequest.cs
@@ -0,0 +1,331 @@
+//作者:Mcdull
+//说明:FTP命令请求接收处理类
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+
+namespace FtpServer
+{
+ class FtpRequest : IDisposable
+ {
+ private RequestType transferType;
+ private string fileName;
+ private FtpClient client;
+
+ private TcpListener PASV_listener; //PASV模式启用监听
+ private readonly IPAddress PASV_PROXY_IP;
+ private readonly int PASV_PROXY_PORT;
+ private readonly IPAddress PASV_IP;
+ private readonly int PASV_PORT = 5397;
+ private IPAddress PORT_IP; //PORT模式记录客户端监听地址和端口
+ private int PORT_PORT;
+
+ public FtpRequest(FtpClient client, IPAddress pasv_ip, int pasv_port)
+ {
+ transferType = RequestType.PORT;
+ this.client = client;
+ this.PASV_IP = this.PASV_PROXY_IP = pasv_ip;
+ this.PASV_PORT = this.PASV_PROXY_PORT = pasv_port;
+ }
+
+ public FtpRequest(FtpClient client, IPAddress pasv_ip, int pasv_port, IPAddress pasv_proxy_ip, int pasv_proxy_port) : this(client, pasv_ip, pasv_port)
+ {
+ this.PASV_PROXY_IP = pasv_proxy_ip;
+ this.PASV_PROXY_PORT = pasv_proxy_port;
+ }
+
+ public RequestType Handle(string[] tokens)
+ {
+ if (tokens == null || tokens.Length < 1)
+ {
+ return RequestType.ERROR;
+ }
+ for (int i = 0; i < tokens.Length; i++)
+ {
+ tokens[i] = tokens[i].Trim("\0".ToCharArray());
+ tokens[i] = tokens[i].Trim("\r\n".ToCharArray());
+ }
+ if (tokens[0].ToUpper().IndexOf("XPWD") > -1)
+ tokens[0] = "XPWD";
+ Console.WriteLine("处理命令:" + tokens[0]);
+ switch (tokens[0].ToUpper())
+ {
+ case "USER":
+ client.user.username = tokens[1];
+ return RequestType.LOGIN_USER;
+ case "PASS":
+ client.user.password = tokens[1];
+ return RequestType.LOGIN_PASS;
+ case "SYST":
+ client.SendMessage("215 " + Environment.OSVersion.ToString());
+ return RequestType.SYSTEM;
+ case "OPTS":
+ client.SendMessage("200 设置成功");
+ var args = tokens[1].Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ client.UpdateEncode(args[0], args[1]);
+ return RequestType.OPTS;
+ case "RETR":
+ fileName = tokens[1];
+ byte[] file = client.GetFile(fileName);
+ if (file == null)
+ {
+ client.SendMessage("550 文件不存在");
+ }
+ else if (file.Length == 0)
+ {
+ client.SendMessage("550 文件大小为空");
+ }
+ else
+ {
+ client.SendMessage("150 开始传输数据");
+ client.SendMessageByTempSocket(getTempSocket(), file);
+ client.SendMessage("226 文件发送完毕");
+ }
+ return RequestType.RETRIEVE;
+ case "STOR":
+ fileName = tokens[1];
+ client.SendMessage("150 开始传输数据");
+ if (client.ReceiveFile(getTempSocket(), fileName))
+ client.SendMessage("226 文件接受完毕");
+ else
+ client.SendMessage("550 不能上传文件(文件可能已存在)");
+ return RequestType.STORE;
+ case "RNFR":
+ fileName = tokens[1];
+ client.SendMessage("350");
+ return RequestType.RENAME_FROM;
+ case "RNTO":
+ int r = client.Rename(fileName, tokens[1]);
+ fileName = tokens[1];
+ switch (r)
+ {
+ case 1:
+ client.SendMessage("250 目录改名成功");
+ break;
+ case 2:
+ client.SendMessage("250 文件改名成功");
+ break;
+ default:
+ client.SendMessage("550 目录或者文件不存在");
+ break;
+ }
+ return RequestType.RENAME_TO;
+ case "XMKD":
+ case "MKD":
+ fileName = tokens[1];
+ if (!client.CreateDir(fileName))
+ client.SendMessage("221 目录已经存在");
+ else
+ client.SendMessage("250 目录创建成功");
+ return RequestType.XMKD;
+ case "DELE":
+ fileName = tokens[1];
+ switch (client.Delete(fileName))
+ {
+ case 1:
+ client.SendMessage("250 目录删除成功");
+ break;
+ case 2:
+ client.SendMessage("250 文件删除成功");
+ break;
+ default:
+ client.SendMessage("221 目录或者文件不存在");
+ break;
+ }
+ return RequestType.DELETE;
+ case "PWD":
+ case "XPWD":
+ client.SendMessage("257 当前目录\"" + client.GetCurrentDir() + "\"");
+ return RequestType.PWD;
+ case "LIST":
+ case "NLST":
+ client.SendMessage("150 显示目录信息");
+ client.SendMessageByTempSocket(getTempSocket(), client.GetCurrentDirList());
+ client.SendMessage("226 显示完毕");
+ return RequestType.LIST;
+ case "CWD":
+ fileName = tokens[1];
+ if (client.GotoDir(fileName))
+ client.SendMessage("257 当前目录\"" + client.GetCurrentDir() + "\"");
+ else
+ client.SendMessage("500 目录不存在");
+ return RequestType.CWD;
+ case "CDUP":
+ if (client.GotoDir(".."))
+ client.SendMessage("257 当前目录\"" + client.GetCurrentDir() + "\"");
+ else
+ client.SendMessage("500 目录不存在");
+ return RequestType.CDUP;
+ case "NOOP":
+ client.SendMessage("200 NOOP命令成功");
+ return RequestType.NOOP;
+ case "QUIT":
+ client.SendMessage("221 退出登录");
+ client.LoginOut();
+ return RequestType.LOGOUT;
+ case "PORT":
+ {
+ transferType = RequestType.PORT;
+ string[] data = new string[6];
+ if (tokens.Length == 2)
+ data = tokens[1].Split(new char[] { ',' });
+ //else if (tokens.Length == 3)
+ // data = tokens[2].Split(new char[] { ',' });
+ else
+ throw new ArgumentException("PORT命令参数无效");
+ PORT_PORT = (Int32.Parse(data[4]) << 8) + Int32.Parse(data[5]);
+ PORT_IP = IPAddress.Parse(data[0] + "." + data[1] + "." + data[2] + "." + data[3]);
+ client.SendMessage("200");
+ return RequestType.DATA_PORT;
+ }
+ case "PASV":
+ {
+ transferType = RequestType.PASV;
+ if (PASV_listener == null)
+ {
+ PASV_listener = new TcpListener(PASV_IP, PASV_PORT);
+ PASV_listener.Start();
+ }
+ string ip = string.Format("{0},{1},{2}", PASV_PROXY_IP.ToString().Replace('.', ','), PASV_PROXY_PORT >> 8, PASV_PROXY_PORT & 0xff);
+ client.SendMessage(string.Format("227 Entering Passive Mode ({0})", ip));
+ return RequestType.PASSIVE;
+ }
+ default:
+ client.SendMessage("221 未知的命令" + tokens[0].ToUpper());
+ return RequestType.UNKNOWN_CMD;
+ }
+ }
+
+ private Socket getTempSocket()
+ {
+ Socket tempSocket = null;
+ if (transferType == RequestType.PASV)
+ {
+ int timeout = 5000;
+ while (timeout-- > 0)
+ {
+ if (PASV_listener.Pending())
+ {
+ tempSocket = PASV_listener.AcceptSocket();
+ break;
+ }
+ System.Threading.Thread.Sleep(500);
+ }
+ }
+ else
+ {
+ tempSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ tempSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);
+ IPEndPoint hostEndPoint = new IPEndPoint(PORT_IP, PORT_PORT);
+ try
+ {
+ tempSocket.Connect(hostEndPoint);
+ }
+ catch (SocketException ex)
+ {
+ Console.WriteLine("PORT连接失败:{0}", ex.Message);
+ return null;
+ }
+ }
+ return tempSocket;
+ }
+
+ public void Dispose()
+ {
+ if (PASV_listener != null)
+ {
+ PASV_listener.Stop();
+ }
+ }
+ }
+
+ enum RequestType
+ {
+ ///
+ /// 发送登录名
+ ///
+ LOGIN_USER,
+ ///
+ /// 发送登录密码
+ ///
+ LOGIN_PASS,
+ ///
+ /// 请求系统类型
+ ///
+ SYSTEM,
+ RESTART,
+ ///
+ /// 请求获得文件
+ ///
+ RETRIEVE,
+ ///
+ /// 存储文件
+ ///
+ STORE,
+ ///
+ /// 要重命名的文件
+ ///
+ RENAME_FROM,
+ ///
+ /// 重命名为新文件
+ ///
+ RENAME_TO,
+ ABORT,
+ ///
+ /// 删除文件
+ ///
+ DELETE,
+ ///
+ /// 创建目录
+ ///
+ XMKD,
+ ///
+ /// 显示当前工作目录
+ ///
+ PWD,
+ ///
+ /// 请求获得目录信息
+ ///
+ LIST,
+ ///
+ /// 等待(NOOP) 此命令不产生什么实际动作,它仅使服务器返回OK。
+ ///
+ NOOP,
+ ///
+ /// 表示类型
+ ///
+ REPRESENTATION_TYPE,
+ ///
+ /// 退出登录
+ ///
+ LOGOUT,
+ ///
+ /// 客户端告知服务器端口
+ ///
+ DATA_PORT,
+ ///
+ /// 采用PASV传输方法(理解为客户端不发送PORT命令,即服务器不能确定客户端口)
+ ///
+ PASSIVE,
+ ///
+ /// 改变工作目录
+ ///
+ CWD,
+ ///
+ /// 返回上级目录
+ ///
+ CDUP,
+ ///
+ /// 设置传输编码
+ ///
+ OPTS,
+ CHANGE_DIR_UP,
+ UNKNOWN_CMD,
+ PORT,
+ PASV,
+ ERROR
+ }
+}
diff --git a/plugins/NSmartProxyFTP/FtpServer.cs b/plugins/NSmartProxyFTP/FtpServer.cs
new file mode 100644
index 0000000..1826612
--- /dev/null
+++ b/plugins/NSmartProxyFTP/FtpServer.cs
@@ -0,0 +1,117 @@
+//作者:Mcdull
+//说明:FTP服务端类,负责监听端口并建立连接。
+//此处单独封装一个类是为了和UI层分离,使得UI仅需通过捕获事件获取通知而无须关心逻辑
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net.Sockets;
+using System.Threading;
+using System.Net;
+
+namespace FtpServer
+{
+ class FtpServer
+ {
+ private TcpListener listener;
+ private bool isStart;
+ private Thread mainThread;
+ private int maxConnect;
+
+ private IPAddress LocalIP { get; set; }
+ private int Port { get; set; }
+ private int PasvPort { get; set; }
+ public static IEnumerable Users { get; private set; }
+
+ public FtpServer(int port, int pasv_port, int max_connect, IEnumerable users)
+ {
+ clients = new List();
+ // listener = new TcpListener(ip, port);
+ listener = new TcpListener(IPAddress.Any, port);
+ maxConnect = max_connect;
+ // LocalIP = ip;
+ LocalIP = IPAddress.Any;
+ Port = port;
+ PasvPort = pasv_port;
+ Users = users;
+ }
+
+ public List clients;
+ public event Action clientChange;
+
+ public bool Start(IPAddress pasv_proxy_ip, int pasv_proxy_port)
+ {
+ if (!isStart)
+ {
+ mainThread = new Thread(p =>
+ {
+ while (isStart)
+ {
+ if (clients.Count < maxConnect)
+ {
+ try
+ {
+ listener.Start();
+ Socket socket = listener.AcceptSocket();
+ FtpClient client = new FtpClient(socket, LocalIP, PasvPort, pasv_proxy_ip, pasv_proxy_port);
+ client.Quit += client_Quit;
+ client.Login += client_Login;
+ clients.Add(client);
+ onClientChange(client);
+ client.Start();
+ }
+ catch
+ {
+ isStart = false;
+ return;
+ }
+ }
+ else
+ {
+ listener.Stop();
+ }
+ Thread.Sleep(2000);
+ }
+ });
+ mainThread.IsBackground = true; //设置为后台线程,进程关闭后线程强制关闭(前台线程则会导致线程结束后进程才能结束)
+ mainThread.Start();
+ isStart = true;
+ return true;
+ }
+ return false;
+ }
+
+ public bool Stop()
+ {
+ if (isStart)
+ {
+ isStart = false;
+ listener.Stop();
+ mainThread.Abort();
+ foreach (var c in clients)
+ c.Stop();
+ clients.Clear();
+ return true;
+ }
+ return false;
+ }
+
+ void client_Login(FtpClient client)
+ {
+ onClientChange(client);
+ }
+
+ void client_Quit(FtpClient client)
+ {
+ clients.Remove(client);
+ onClientChange(client);
+ }
+
+ private void onClientChange(FtpClient client)
+ {
+ var temp = clientChange;
+ if (temp != null)
+ temp(client);
+ }
+ }
+}
diff --git a/plugins/NSmartProxyFTP/FtpUser.cs b/plugins/NSmartProxyFTP/FtpUser.cs
new file mode 100644
index 0000000..97ce37c
--- /dev/null
+++ b/plugins/NSmartProxyFTP/FtpUser.cs
@@ -0,0 +1,34 @@
+//作者:Mcdull
+//说明:FTP账号类
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+
+namespace FtpServer
+{
+ sealed class User
+ {
+ public bool isLogin { get; set; }
+ public string username { get; set; }
+ public string password { get; set; }
+ public string workingDir { get; set; }
+ }
+
+ sealed class UserElement
+ {
+ public string username
+ {
+ get; set;
+ }
+ public string password
+ {
+ get; set;
+ }
+ public string rootDir
+ {
+ get; set;
+ }
+ }
+}
diff --git a/plugins/NSmartProxyFTP/NSmartProxyFTP.csproj b/plugins/NSmartProxyFTP/NSmartProxyFTP.csproj
new file mode 100644
index 0000000..e2d76d8
--- /dev/null
+++ b/plugins/NSmartProxyFTP/NSmartProxyFTP.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/plugins/NSmartProxyFTP/Program.cs b/plugins/NSmartProxyFTP/Program.cs
new file mode 100644
index 0000000..7fed17a
--- /dev/null
+++ b/plugins/NSmartProxyFTP/Program.cs
@@ -0,0 +1,157 @@
+using log4net;
+using log4net.Config;
+using Microsoft.Extensions.Configuration;
+using NSmartProxy.Client;
+using NSmartProxy.Data;
+using NSmartProxy.Data.Models;
+using NSmartProxy.Interfaces;
+using NSmartProxy.Shared;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using FtpServer;
+using System.Net;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+
+namespace NSmartProxyFTP
+{
+ class Program
+ {
+ #region logger
+ public class Log4netLogger : INSmartLogger
+ {
+ public void Debug(object message)
+ {
+ //Logger.Debug(message);
+ Logger.Debug(message);
+ }
+
+ public void Error(object message, Exception ex)
+ {
+ //Logger.Debug(message);
+ Logger.Error(message, ex);
+ }
+
+ public void Info(object message)
+ {
+ Logger.Info(message);
+ }
+ }
+ #endregion
+
+ public static ILog Logger;
+ public static IConfigurationRoot Configuration { get; set; }
+ private static LoginInfo _currentLoginInfo;
+ static void Main(string[] args)
+ {
+ //log
+ var loggerRepository = LogManager.CreateRepository("NSmartClientRouterRepository");
+ XmlConfigurator.Configure(loggerRepository, new FileInfo("log4net.config"));
+ Logger = LogManager.GetLogger(loggerRepository.Name, "NSmartProxyFTP");
+ if (!loggerRepository.Configured) throw new Exception("log config failed.");
+ Console.ForegroundColor = ConsoleColor.Yellow;
+
+ //用户登录
+ if (args.Length == 4)
+ {
+ _currentLoginInfo = new LoginInfo();
+ _currentLoginInfo.UserName = args[1];
+ _currentLoginInfo.UserPwd = args[3];
+ }
+
+ Logger.Info($"*** {NSPVersion.NSmartProxyServerName} ***");
+
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json");
+
+ Configuration = builder.Build();
+
+ //start clientrouter.
+ try
+ {
+ StartClient().Wait();
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e.Message);
+ }
+ Console.Read();
+ Logger.Info("Client terminated,press any key to continue.");
+
+ }
+
+ private static void StartFTP(ClientModel clientModel)
+ {
+ var ip = GetIP(Configuration.GetSection("ProviderAddress").Value);
+ Console.WriteLine("外网IP:" + ip.ToString());
+ var users = new List();
+ foreach (var u in Configuration.GetSection("FtpUsers").GetChildren())
+ {
+ users.Add(new UserElement() { username = u["username"], password = u["password"], rootDir = u["rootDir"] });
+ }
+ var server = new FtpServer.FtpServer(int.Parse(Configuration.GetSection("FtpPort").Value), int.Parse(Configuration.GetSection("PasvPort").Value), int.Parse(Configuration.GetSection("FtpMaxConnect").Value), users);
+ server.Start(ip, clientModel.AppList[1].Port);
+ }
+
+ private static IPAddress GetIP(string server)
+ {
+ Regex rx = new Regex(@"((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))");
+ if (!rx.IsMatch(server))
+ {
+ IPAddress[] ips = Dns.GetHostAddresses(server);
+ return ips[0];
+ }
+ return IPAddress.Parse(server);
+ }
+
+ private static async Task StartClient()
+ {
+
+ Router clientRouter = new Router(new Log4netLogger());
+ //read config from config file.
+ SetConfig(clientRouter);
+ if (_currentLoginInfo != null)
+ {
+ clientRouter.SetLoginInfo(_currentLoginInfo);
+ }
+
+ Task tsk = clientRouter.Start(true, StartFTP);
+ try
+ {
+ await tsk;
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e);
+ throw;
+ }
+
+ }
+
+ private static void SetConfig(Router clientRouter)
+ {
+
+ NSPClientConfig config = new NSPClientConfig();
+ config.ProviderAddress = Configuration.GetSection("ProviderAddress").Value;
+ config.ProviderWebPort = int.Parse(Configuration.GetSection("ProviderWebPort").Value);
+ var configClients = Configuration.GetSection("Clients").GetChildren();
+ foreach (var cli in configClients)
+ {
+ int confConsumerPort = 0;
+ if (cli["ConsumerPort"] != null) confConsumerPort = int.Parse(cli["ConsumerPort"]);
+ config.Clients.Add(new ClientApp
+ {
+ IP = cli["IP"],
+ TargetServicePort = int.Parse(cli["TargetServicePort"]),
+ ConsumerPort = confConsumerPort,
+ Host = cli["Host"],
+ Protocol = Enum.Parse((cli["Protocol"] ?? "TCP").ToUpper()),
+ Description = cli["Description"]
+ });
+ }
+ clientRouter.SetConfiguration(config);
+ }
+ }
+}
diff --git a/plugins/NSmartProxyFTP/appsettings.json b/plugins/NSmartProxyFTP/appsettings.json
new file mode 100644
index 0000000..3e861f5
--- /dev/null
+++ b/plugins/NSmartProxyFTP/appsettings.json
@@ -0,0 +1,39 @@
+{
+ "ProviderWebPort": 12309,
+ "ProviderAddress": "2017studio.imwork.net",
+ "FtpPort": 5555,
+ "PasvPort": 5379,
+ "FtpMaxConnect": 2, //FTP最大连接数
+ //FTP用户,可以配置多个
+ "FtpUsers": [
+ {
+ "username": "admin",
+ "password": "654123",
+ "rootDir": "d:\\"
+ }
+ ],
+
+ //反向代理客户端,可以配置多个
+ "Clients": [
+
+ //{
+ // "IP": "127.0.0.1",
+ // "TargetServicePort": "82",
+ // "Host": "haha.tmoonlight.top",
+ // "Protocol": "HTTP",
+ // "ConsumerPort": "443",
+ // "Description": "测试备注"
+ //},
+
+ {
+ "IP": "127.0.0.1",
+ "TargetServicePort": "5555",
+ "ConsumerPort": "20006"
+ },
+ {
+ "IP": "127.0.0.1",
+ "TargetServicePort": "5379",
+ "ConsumerPort": "20007"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/plugins/NSmartProxyFTP/log4net.config b/plugins/NSmartProxyFTP/log4net.config
new file mode 100644
index 0000000..7ea9795
--- /dev/null
+++ b/plugins/NSmartProxyFTP/log4net.config
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sh.exe.stackdump b/sh.exe.stackdump
new file mode 100644
index 0000000..426b8b7
--- /dev/null
+++ b/sh.exe.stackdump
@@ -0,0 +1,11 @@
+Stack trace:
+Frame Function Args
+00000010002 0018006021E (00180241C10, 001802340B9, 00000010002, 000FFFFBA20)
+00000010002 00180048859 (00000000002, 00180329138, 00000000002, 00180329138)
+00000010002 00180048892 (00000000002, 00180329448, 00000010002, 00000000008)
+00000010002 001800598BC (000FFFFCC72, 000FFFFCC55, 00000000000, 001802343AF)
+000FFFFCCD0 00180059960 (645C655C725C635C, 695C745C6E5C655C, 6D5C2D5C6C5C615C, 675C615C6E5C615C)
+000FFFFCCD0 00180048FE1 (00000000000, 00000000000, 00000000000, 00000000000)
+00000000000 00180047963 (00000000000, 00000000000, 00000000000, 00000000000)
+000FFFFFFF0 00180047A14 (00000000000, 00000000000, 00000000000, 00000000000)
+End of stack trace
diff --git a/src/NSmartProxy.ClientRouter/Authorize/UserCacheManager.cs b/src/NSmartProxy.ClientRouter/Authorize/UserCacheManager.cs
new file mode 100644
index 0000000..0479c10
--- /dev/null
+++ b/src/NSmartProxy.ClientRouter/Authorize/UserCacheManager.cs
@@ -0,0 +1,122 @@
+using System;
+using System.IO;
+using NSmartProxy.Data.Models;
+
+namespace NSmartProxy.Client.Authorize
+{
+ public class UserCacheManager
+ {
+ //private Router router;
+ //private ClientUserCache clientUserCache;
+ //private string cachePath;
+
+ //public ClientUserCacheItem GetCurrentUserCache()
+ //{
+ // return clientUserCache.TryGetValue(router.ClientConfig.ProviderAddress + ":" +
+ // router.ClientConfig.ProviderWebPort, out var userCache) ? userCache : null;
+ //}
+
+ //private UserCacheManager()
+ //{
+ //}
+
+ ///
+ /// 通过地址获取用户信息
+ ///
+ ///
+ ///
+ ///
+ public static ClientUserCacheItem GetUserCacheFromEndpoint(string endpoint, string cachePath)
+ {
+ ClientUserCache userCache = GetClientUserCache(cachePath);
+ if (userCache.ContainsKey(endpoint))
+ {
+ return userCache[endpoint];
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /////
+ ///// 初始化
+ /////
+ /////
+ /////
+ /////
+ //public static UserCacheManager Init(Router pRouter, string cachePath)
+ //{
+ // ClientUserCache userCache = GetClientUserCache(cachePath);
+ // var userCacheManager = new UserCacheManager
+ // {
+ // router = pRouter,
+ // clientUserCache = userCache
+ // };
+ // return userCacheManager;
+ //}
+
+ ///
+ /// 获取整个缓存集合
+ ///
+ ///
+ ///
+ public static ClientUserCache GetClientUserCache(string cachePath)
+ {
+ ClientUserCache userCache;
+ if (!File.Exists(cachePath))
+ {
+ File.Create(cachePath).Close();
+ }
+
+ try
+ {
+ userCache = File.ReadAllText(cachePath).ToObject();
+ }
+ catch //(Exception e)
+ {
+ // Console.WriteLine(e);
+ userCache = null;
+ }
+
+ if (userCache == null)
+ {
+ userCache = new ClientUserCache();
+ }
+
+ return userCache;
+ }
+
+ ///
+ /// 保存文件
+ ///
+ ///
+ ///
+ public static void SaveChanges(string cachePath, ClientUserCache clientUserCache)
+ {
+ File.WriteAllText(cachePath, clientUserCache.ToJsonString());
+ }
+
+ ///
+ /// 更新特定的用户
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void UpdateUser(string token, string userName, string serverEndPoint, string cachePath)
+ {
+ var clientUserCache = GetClientUserCache(cachePath);
+ if (!clientUserCache.ContainsKey(serverEndPoint))
+ {
+ clientUserCache[serverEndPoint] = new ClientUserCacheItem();
+ }
+
+ var item = clientUserCache[serverEndPoint];
+ item.Token = token;
+ item.UserName = userName;
+ SaveChanges(cachePath, clientUserCache);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NSmartProxy.ClientRouter/ClientAppWorker.cs b/src/NSmartProxy.ClientRouter/ClientAppWorker.cs
index 6de60f3..1d36245 100644
--- a/src/NSmartProxy.ClientRouter/ClientAppWorker.cs
+++ b/src/NSmartProxy.ClientRouter/ClientAppWorker.cs
@@ -10,7 +10,7 @@ public class ClientAppWorker
//private bool isWorking = false;
//public List TcpClientGroup = new List();
- public TcpClient Client;
+ public TcpClient Client;//TODO 还是需要把这里改成复数
public int AppId; //1~255
public int Port; //0~65535
diff --git a/src/NSmartProxy.ClientRouter/Dispatchers/NSPDispatcher.cs b/src/NSmartProxy.ClientRouter/Dispatchers/NSPDispatcher.cs
index f4f5cf3..3dfab7b 100644
--- a/src/NSmartProxy.ClientRouter/Dispatchers/NSPDispatcher.cs
+++ b/src/NSmartProxy.ClientRouter/Dispatchers/NSPDispatcher.cs
@@ -1,27 +1,51 @@
using System;
using System.Collections.Generic;
+using System.Net;
using System.Net.Http;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using NSmartProxy.Data;
-using NSmartProxy.Data.DTO;
+using NSmartProxy.Data.DTOs;
namespace NSmartProxy.ClientRouter.Dispatchers
{
public class NSPDispatcher
{
- private string BaseUrl = "localhost";
+ private string BaseUrl;
+ //TODO httpclient的一种解决方案:定时对象
+ private static HttpClient _client;
+ private static Timer _timer = new Timer(obj =>
+ {
+ _client?.Dispose();
+ _client = null;
+ });
+
+ //_client.Dispose();_client = null
+
public NSPDispatcher(string baseUrl)
{
BaseUrl = baseUrl;
}
+ public static HttpClient Client
+ {
+ get
+ {
+ if (_client == null)
+ {
+ //_timer = new
+ _client = new HttpClient();
+ }
+ return _client;
+ }
+ }
+
public async Task> LoginFromClient(string username, string userpwd)
{
string url = $"http://{BaseUrl}/LoginFromClient";
- HttpClient client = new HttpClient();
- var httpmsg = await client.GetAsync($"{url}?username={username}&userpwd={userpwd}").ConfigureAwait(false);
+ var httpmsg = await Client.GetAsync($"{url}?username={username}&userpwd={userpwd}").ConfigureAwait(false);
var httpstr = await httpmsg.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject>(httpstr);
}
@@ -29,14 +53,46 @@ public async Task> LoginFromClient(string user
public async Task> Login(string userid, string userpwd)
{
string url = $"http://{BaseUrl}/LoginFromClientById";
- HttpClient client = new HttpClient();
- var httpmsg = await client.GetAsync($"{url}?username={userid}&userpwd={userpwd}").ConfigureAwait(false);
+ var httpmsg = await Client.GetAsync($"{url}?username={userid}&userpwd={userpwd}").ConfigureAwait(false);
var httpstr = await httpmsg.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject>(httpstr);
}
//TODO 增加一个校验用户token是否合法的方法
//public
+ //GetServerPorts
+ public async Task> GetServerPorts()
+ {
+ string url = $"http://{BaseUrl}/GetServerPorts";
+ var httpmsg = await Client.GetAsync(url).ConfigureAwait(false);
+ var httpstr = await httpmsg.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return JsonConvert.DeserializeObject>(httpstr);
+ }
+
+ ///
+ /// 当允许服务端端修改客户端时,从服务端获取配置
+ ///
+ ///
+ public async Task> GetServerClientConfig(string token)
+ {
+ string url = $"http://{BaseUrl}/GetServerClientConfig";
+
+ CookieContainer cookieContainer = new CookieContainer();
+ Cookie cookie = new Cookie("NSPTK", token);
+ cookie.Domain = BaseUrl.Substring(0, BaseUrl.IndexOf(':'));
+ cookieContainer.Add(cookie); // 加入Cookie
+ HttpClientHandler httpClientHandler = new HttpClientHandler()
+ {
+ CookieContainer = cookieContainer,
+ AllowAutoRedirect = true,
+ UseCookies = true
+ };
+
+ HttpClient cookieClient = new HttpClient(httpClientHandler);
+ var httpmsg = await cookieClient.GetAsync($"{url}?userid=").ConfigureAwait(false);
+ var httpstr = await httpmsg.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return JsonConvert.DeserializeObject>(httpstr);
+ }
}
}
diff --git a/src/NSmartProxy.ClientRouter/NSmartProxy.ClientRouter.csproj b/src/NSmartProxy.ClientRouter/NSmartProxy.ClientRouter.csproj
index 69ad6b9..58102fb 100644
--- a/src/NSmartProxy.ClientRouter/NSmartProxy.ClientRouter.csproj
+++ b/src/NSmartProxy.ClientRouter/NSmartProxy.ClientRouter.csproj
@@ -1,7 +1,7 @@
- netstandard2.0
+ net6.0;netstandard2.0
diff --git a/src/NSmartProxy.ClientRouter/Router.cs b/src/NSmartProxy.ClientRouter/Router.cs
index 08338cd..534cb87 100644
--- a/src/NSmartProxy.ClientRouter/Router.cs
+++ b/src/NSmartProxy.ClientRouter/Router.cs
@@ -10,8 +10,11 @@
using System.Threading.Tasks;
using NSmartProxy.Shared;
using System.IO;
+using System.Reflection;
+using NSmartProxy.Client.Authorize;
using NSmartProxy.ClientRouter.Dispatchers;
using NSmartProxy.Data.Models;
+using NSmartProxy.Infrastructure;
namespace NSmartProxy.Client
{
@@ -42,12 +45,17 @@ public enum ClientStatus
public class Router
{
- public const string NSMART_CLIENT_CACHE_PATH = "./cli_cache_v2.cache";
- CancellationTokenSource ONE_LIVE_TOKEN_SRC;
- CancellationTokenSource CANCEL_TOKEN_SRC;
- CancellationTokenSource TRANSFERING_TOKEN_SRC;
- CancellationTokenSource HEARTBEAT_TOKEN_SRC;
- TaskCompletionSource