├── .editorconfig
├── .gitignore
├── Doc
└── Demo.png
├── HttpFilter
├── ContentFilter.cs
├── HttpFilter.csproj
├── IPFilter.cs
└── Properties
│ └── AssemblyInfo.cs
├── LICENSE
├── Readme.MD
├── Test
├── Program.cs
└── Test.csproj
├── XP
├── FrmMain.Designer.cs
├── FrmMain.cs
├── FrmMain.resx
├── ListenerManage.Designer.cs
├── ListenerManage.cs
├── ListenerManage.resx
├── Program.cs
├── ProjectInstaller.Designer.cs
├── ProjectInstaller.cs
├── ProjectInstaller.resx
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── XP.csproj
├── XProxySvc.Designer.cs
├── XProxySvc.cs
├── app.config
├── leaf.ico
└── packages.config
├── XProxy.sln
└── XProxy
├── Base
├── Connection.cs
├── Listener.cs
└── Session.cs
├── ByteHelper.cs
├── Config
├── ListenerConfig.cs
├── PluginConfig.cs
├── PluginSelectorForm.Designer.cs
├── PluginSelectorForm.cs
├── PluginSelectorForm.resx
└── ProxyConfig.cs
├── Http
├── HttpBase.cs
├── HttpCache.cs
├── HttpHeader.cs
├── HttpHelper.cs
├── HttpPlugin.cs
├── HttpPluginBase.cs
├── HttpRequest.cs
├── HttpResponse.cs
├── IHttpPlugin.cs
└── Plugin
│ ├── Direct.cs
│ ├── HttpCache.cs
│ ├── Indirect.cs
│ └── Reverse.cs
├── NetData.cs
├── NetHelper.cs
├── Plugin
├── EncryptPlugin.cs
├── IPlugin.cs
├── NATPlugin.cs
├── PluginBase.cs
└── PluginManager.cs
├── Program.cs
├── Properties
├── AssemblyInfo.cs
└── PublishProfiles
│ └── FolderProfile.pubxml
├── Proxy
├── HttpIpProxy.cs
├── HttpProxy.cs
├── HttpReverseProxy.cs
├── NATProxy.cs
├── NATSession.cs
├── ProxyBase.cs
├── ProxySession.cs
└── Socks5
│ ├── Socks5Client.cs
│ ├── Socks5Entity.cs
│ └── Socks5Server.cs
├── ProxyHelper.cs
├── Setting.cs
├── XProxy.csproj
└── leaf.ico
/.editorconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/.editorconfig
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
3 | ################################################################################
4 |
5 | /.vs
6 | [Dd]ebug/
7 | [Dd]ebugPublic/
8 | [Rr]elease/
9 | [Rr]eleases/
10 | x64/
11 | x86/
12 | build/
13 | bld/
14 | [Bb]in/
15 | [Oo]bj/
16 | /packages
17 | *.user
18 | /Data
19 | /Log
20 | /NewLife.Cube/Config
21 | /NewLife.Cube/Plugins
22 | *.log
23 | *.htm
24 | *.nuspec
25 | *.nupkg
26 | /NewLife.Core.Core/project.lock.json
27 | /Backup
28 | /ConsoleApp1
29 | /NewLife.Cube4/Config
30 | /NewLife.Cube4/Content
31 | /NewLife.Cube4/Plugins
32 | /XCode/Membership/Config
33 | /XCode/Sharding/Config
34 | /XCode/Demo/Config
35 | /XCode/Membership
36 | /Keys
37 | /Avatars
38 | /BinServer
39 | /BinTest
40 |
--------------------------------------------------------------------------------
/Doc/Demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/Doc/Demo.png
--------------------------------------------------------------------------------
/HttpFilter/ContentFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Text.RegularExpressions;
4 | using XProxy.Base;
5 | using XProxy.Config;
6 | using XProxy.Http;
7 |
8 | namespace HttpFilter
9 | {
10 | ///
11 | /// 内容过滤器
12 | ///
13 | public class ContentFilter : HttpPluginBase
14 | {
15 | ///
16 | /// 处理响应后的内容。
17 | /// 现在的延迟处理还没有做好,如果做好了延迟处理,就可以直接OnResponse的另一个版本。
18 | ///
19 | ///
20 | ///
21 | ///
22 | public override Byte[] OnResponseContent(Session client, Byte[] Data)
23 | {
24 | //要注意编码。不一定是gb2312编码。V1.0版中有自动识别编码的功能,现在还没有迁移过来。
25 | var str = Encoding.Default.GetString(Data);
26 | str = Regex.Replace(str, @"\w+\.baidu\.com",
27 | Manager.Manager.Listener.Address.ToString() + ":" +
28 | Manager.Manager.Listener.Config.Port.ToString());
29 | return Encoding.Default.GetBytes(str);
30 | }
31 |
32 | public override PluginConfig DefaultConfig
33 | {
34 | get
35 | {
36 | var pc = base.DefaultConfig;
37 | pc.Name = "内容过滤器";
38 | pc.Author = "大石头";
39 | return pc;
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/HttpFilter/HttpFilter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 | AnyCPU
5 | 9.0.21022
6 | 2.0
7 | {33BA8DD2-E0E7-4605-A074-109222C39FD9}
8 | Library
9 | Properties
10 | HttpFilter
11 | HttpFilter
12 | SAK
13 | SAK
14 | SAK
15 | SAK
16 |
17 |
18 | 3.5
19 |
20 |
21 | v4.6.1
22 |
23 |
24 |
25 | true
26 | full
27 | false
28 | ..\..\发布\
29 | DEBUG;TRACE
30 | prompt
31 | 4
32 | false
33 |
34 |
35 | pdbonly
36 | true
37 | ..\..\生成\
38 | TRACE
39 | prompt
40 | 4
41 | false
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}
56 | XProxy
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/HttpFilter/IPFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using XProxy.Base;
3 | using XProxy.Config;
4 | using XProxy.Plugin;
5 |
6 | namespace HttpFilter
7 | {
8 | ///
9 | /// IP过滤器。禁止某些IP访问。
10 | /// 本例子演示了禁止127.0.0.1访问,把该插件放在反向代理插件之前,即可禁止127.0.0.1使用反向代理。
11 | ///
12 | public class IPFilter : PluginBase
13 | {
14 | public override Boolean OnClientStart(Session client)
15 | {
16 | var ip = client.IPAndPort;
17 | if (ip.IndexOf(":") > 0) ip = ip.Substring(0, ip.IndexOf(":"));
18 | return ip != "127.0.0.1";
19 | }
20 |
21 | public override PluginConfig DefaultConfig
22 | {
23 | get
24 | {
25 | var pc = base.DefaultConfig;
26 | pc.Name = "IP过滤器";
27 | pc.Author = "大石头";
28 | return pc;
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/HttpFilter/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过下列属性集
6 | // 控制。更改这些属性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("HttpFilter")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("NewLife")]
12 | [assembly: AssemblyProduct("HttpFilter")]
13 | [assembly: AssemblyCopyright("版权所有 (C) NewLife 2008")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 属性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("751810a5-34d8-4664-9580-2005460c28ef")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 内部版本号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“修订号”和“内部版本号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 新生命开发团队
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Readme.MD:
--------------------------------------------------------------------------------
1 | ## XProxy代理服务器
2 | XProxy是一个代理服务器宿主环境,可用于寄宿多种`ProxyBase`服务,主要是`NATProxy`反向代理。
3 |
4 | 2005~2008期间,第一代XProxy同时也是第一代网络库,主要支持NAT、加解密、反向代理、直接代理、间接代理等,以Http代理缓存以及数据拦截修改为主。
5 | 2018年v2.0,采用更成熟稳定且具有千万级吞吐的第三代网络库,主要支持反向代理,转发MySql、Oracle、Redis等。
6 |
7 | ## 使用说明
8 | 双击启动`XProxy.exe`,自动生成配置文件`Config\XProxy.config`,默认如下:
9 | 
10 |
11 | XProxy支持单服务同时寄宿多个代理服务,配置文件的Items下,每一行ProxyItem代表一个代理服务。
12 | 以管理员运行`XProxy.exe`,可以选择把它安装成为Windows服务,并控制它的启动和停止。
13 |
14 | ## 新生命开发团队
15 | 本应用作为[新生命开发团队](https://github.com/NewLifeX)2018年开源路线图的一部分,依赖库:
16 | + [X组件](https://github.com/NewLifeX/X)
17 | + [NewLife.Net网络库](https://github.com/NewLifeX/NewLife.Net)
18 |
19 |
20 | ## 新生命开源项目矩阵
21 | 各项目默认支持net4.5/net4.0/netstandard2.0
22 |
23 | | 项目 | 年份 | 状态 | .NET Core | 说明 |
24 | | :--------------------------------------------------------------: | :---: | :----: | :-------: | --------------------------------------------------- |
25 | | 基础组件 | | | | 支撑其它中间件以及产品项目 |
26 | | [NewLife.Core](https://github.com/NewLifeX/X) | 2002 | 维护中 | √ | 算法、日志、网络、RPC、序列化、缓存、多线程 |
27 | | [XCode](https://github.com/NewLifeX/X) | 2005 | 维护中 | √ | 数据中间件,MySQL、SQLite、SqlServer、Oracle |
28 | | [NewLife.Net](https://github.com/NewLifeX/NewLife.Net) | 2005 | 维护中 | √ | 网络库,千万级吞吐率,学习gRPC、Thrift |
29 | | [NewLife.Cube](https://github.com/NewLifeX/NewLife.Cube) | 2010 | 维护中 | √ | Web魔方,企业级快速开发框架,集成OAuth |
30 | | [NewLife.Agent](https://github.com/NewLifeX/NewLife.Agent) | 2008 | 维护中 | √ | 服务管理框架,Windows服务、Linux的Systemd |
31 | | 中间件 | | | | 对接各知名中间件平台 |
32 | | [NewLife.Redis](https://github.com/NewLifeX/NewLife.Redis) | 2017 | 维护中 | √ | Redis客户端,微秒级延迟,百亿级项目验证 |
33 | | [NewLife.RocketMQ](https://github.com/NewLifeX/NewLife.RocketMQ) | 2018 | 维护中 | √ | 支持Apache RocketMQ和阿里云消息队列,十亿级项目验证 |
34 | | [NewLife.MQTT](https://github.com/NewLifeX/NewLife.MQTT) | 2019 | 维护中 | √ | 物联网消息协议,客户端支持阿里云物联网 |
35 | | [NewLife.LoRa](https://github.com/NewLifeX/NewLife.LoRa) | 2016 | 维护中 | √ | 超低功耗的物联网远程通信协议LoRaWAN |
36 | | [NewLife.Thrift](https://github.com/NewLifeX/NewLife.Thrift) | 2019 | 维护中 | √ | Thrift协议实现 |
37 | | [NewLife.Hive](https://github.com/NewLifeX/NewLife.Hive) | 2019 | 维护中 | √ | 纯托管读写Hive,Hadoop数据仓库,基于Thrift协议 |
38 | | [NoDb](https://github.com/NewLifeX/NoDb) | 2017 | 开发中 | √ | NoSQL数据库,百万级kv读写性能,持久化 |
39 | | [NewLife.Ftp](https://github.com/NewLifeX/NewLife.Ftp) | 2008 | 维护中 | √ | Ftp客户端实现 |
40 | | 产品平台 | | | | 产品平台级,编译部署即用,个性化自定义 |
41 | | [AntJob](https://github.com/NewLifeX/AntJob) | 2019 | 维护中 | √ | 蚂蚁调度系统,大数据实时计算平台 |
42 | | [Stardust](https://github.com/NewLifeX/Stardust) | 2018 | 维护中 | √ | 星尘,微服务平台,分布式平台 |
43 | | [XLink](https://github.com/NewLifeX/XLink) | 2016 | 维护中 | √ | 物联网云平台 |
44 | | [XProxy](https://github.com/NewLifeX/XProxy) | 2005 | 维护中 | √ | 产品级反向代理 |
45 | | [XScript](https://github.com/NewLifeX/XScript) | 2010 | 维护中 | × | C#脚本引擎 |
46 | | [SmartOS](https://github.com/NewLifeX/SmartOS) | 2014 | 维护中 | C++11 | 嵌入式操作系统,完全独立自主,ARM Cortex-M芯片架构 |
47 | | [GitCandy](https://github.com/NewLifeX/GitCandy) | 2015 | 维护中 | × | Git管理系统 |
48 | | 其它 | | | | |
49 | | [XCoder](https://github.com/NewLifeX/XCoder) | 2006 | 维护中 | √ | 码神工具,开发者必备 |
50 | | [XTemplate](https://github.com/NewLifeX/XTemplate) | 2008 | 维护中 | √ | 模版引擎,T4(Text Template)语法 |
51 | | [X组件 .NET2.0](https://github.com/NewLifeX/X_NET20) | 2002 | 存档中 | .NET2.0 | 日志、网络、RPC、序列化、缓存、Windows服务、多线程 |
52 |
53 | QQ群:1600800
54 | 博客:https://nnhy.cnblogs.com
55 |
--------------------------------------------------------------------------------
/Test/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using NewLife;
5 | using NewLife.Log;
6 |
7 | namespace Test
8 | {
9 | internal class Program
10 | {
11 | private static void Main(String[] args)
12 | {
13 | XTrace.UseConsole();
14 |
15 | AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", true);
16 |
17 | try
18 | {
19 | var proxy = new WebProxy("10.0.0.3:8080")
20 | {
21 | Credentials = new NetworkCredential("stone", "newlife"),
22 | //BypassProxyOnLocal = false,
23 | //UseDefaultCredentials = false,
24 | };
25 | var handler = new HttpClientHandler
26 | {
27 | Proxy = proxy,
28 | UseProxy = true,
29 | PreAuthenticate = true,
30 | UseDefaultCredentials = false,
31 | };
32 | var client = new HttpClient(handler);
33 |
34 | var headers = client.DefaultRequestHeaders;
35 | headers.Add("White-Ips", "10.0.0.1,10.0.0.2,10.0.0.3,10.0.0.3,");
36 | headers.Add("Black-Ips", "10.0.0.4,10.0.0.5,10.0.0.8,10.0.0.6,");
37 |
38 | var rs = client.GetAsync("http://myip.ipip.net").Result;
39 | XTrace.WriteLine(rs.Content.ReadAsStringAsync().Result);
40 |
41 | XTrace.WriteLine("Local-Ip: {0}", rs.Headers.GetValues("Local-Ip").Join());
42 | }
43 | catch (Exception ex)
44 | {
45 | XTrace.WriteException(ex);
46 | }
47 |
48 | Console.WriteLine("Hello World!");
49 | Console.ReadLine();
50 | }
51 |
52 | public class MyProxy : IWebProxy
53 | {
54 | public Uri ProxyUri { get; set; }
55 |
56 | public ICredentials Credentials { get; set; }
57 |
58 | public MyProxy(String proxyUri)
59 | {
60 | ProxyUri = new Uri(proxyUri);
61 | }
62 |
63 | public Uri GetProxy(Uri destination) => ProxyUri;
64 |
65 | public Boolean IsBypassed(Uri host) => false;
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/Test/Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | ..\Bin\Test
7 | latest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/XP/ListenerManage.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace XP
2 | {
3 | partial class ListenerManage
4 | {
5 | ///
6 | /// 必需的设计器变量。
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// 清理所有正在使用的资源。
12 | ///
13 | /// 如果应释放托管资源,为 true;否则为 false。
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows 窗体设计器生成的代码
24 |
25 | ///
26 | /// 设计器支持所需的方法 - 不要
27 | /// 使用代码编辑器修改此方法的内容。
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ListenerManage));
33 | this.button1 = new System.Windows.Forms.Button();
34 | this.txtLog = new System.Windows.Forms.RichTextBox();
35 | this.label1 = new System.Windows.Forms.Label();
36 | this.label2 = new System.Windows.Forms.Label();
37 | this.timer1 = new System.Windows.Forms.Timer(this.components);
38 | this.SuspendLayout();
39 | //
40 | // button1
41 | //
42 | this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
43 | this.button1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
44 | this.button1.Location = new System.Drawing.Point(24, 12);
45 | this.button1.Name = "button1";
46 | this.button1.Size = new System.Drawing.Size(97, 42);
47 | this.button1.TabIndex = 0;
48 | this.button1.Text = "开始";
49 | this.button1.UseVisualStyleBackColor = true;
50 | this.button1.Click += new System.EventHandler(this.button1_Click);
51 | //
52 | // txtLog
53 | //
54 | this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
55 | | System.Windows.Forms.AnchorStyles.Left)
56 | | System.Windows.Forms.AnchorStyles.Right)));
57 | this.txtLog.Location = new System.Drawing.Point(12, 78);
58 | this.txtLog.Name = "txtLog";
59 | this.txtLog.Size = new System.Drawing.Size(484, 411);
60 | this.txtLog.TabIndex = 2;
61 | this.txtLog.Text = "";
62 | //
63 | // label1
64 | //
65 | this.label1.AutoSize = true;
66 | this.label1.Location = new System.Drawing.Point(165, 23);
67 | this.label1.Name = "label1";
68 | this.label1.Size = new System.Drawing.Size(65, 12);
69 | this.label1.TabIndex = 3;
70 | this.label1.Text = "活动会话:";
71 | //
72 | // label2
73 | //
74 | this.label2.AutoSize = true;
75 | this.label2.Location = new System.Drawing.Point(234, 23);
76 | this.label2.Name = "label2";
77 | this.label2.Size = new System.Drawing.Size(11, 12);
78 | this.label2.TabIndex = 4;
79 | this.label2.Text = "0";
80 | //
81 | // timer1
82 | //
83 | this.timer1.Enabled = true;
84 | this.timer1.Interval = 1000;
85 | this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
86 | //
87 | // ListenerManage
88 | //
89 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
90 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
91 | this.ClientSize = new System.Drawing.Size(508, 501);
92 | this.Controls.Add(this.label2);
93 | this.Controls.Add(this.label1);
94 | this.Controls.Add(this.txtLog);
95 | this.Controls.Add(this.button1);
96 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
97 | this.Name = "ListenerManage";
98 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
99 | this.Text = "监听器管理";
100 | this.Load += new System.EventHandler(this.ListenerManage_Load);
101 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ListenerManage_FormClosing);
102 | this.ResumeLayout(false);
103 | this.PerformLayout();
104 |
105 | }
106 |
107 | #endregion
108 |
109 | private System.Windows.Forms.Button button1;
110 | private System.Windows.Forms.RichTextBox txtLog;
111 | private System.Windows.Forms.Label label1;
112 | private System.Windows.Forms.Label label2;
113 | private System.Windows.Forms.Timer timer1;
114 |
115 | }
116 | }
--------------------------------------------------------------------------------
/XP/ListenerManage.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XP/ListenerManage.cs
--------------------------------------------------------------------------------
/XP/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.ServiceProcess;
5 | using System.Windows.Forms;
6 | using NewLife.Log;
7 |
8 | namespace XP
9 | {
10 | static class Program
11 | {
12 | ///
13 | /// 应用程序的主入口点。
14 | ///
15 | [STAThread]
16 | static void Main()
17 | {
18 | XTrace.UseWinForm();
19 |
20 | var args = Environment.GetCommandLineArgs();
21 | if (args.Length > 1)
22 | {
23 | if (args[1].ToLower() == "-s") //启动服务
24 | {
25 | var ServicesToRun = new ServiceBase[] { new XProxySvc() };
26 | try
27 | {
28 | ServiceBase.Run(ServicesToRun);
29 | }
30 | catch (Exception ex)
31 | {
32 | XTrace.WriteLine(ex.ToString());
33 | Console.WriteLine(ex.ToString());
34 | }
35 | return;
36 | }
37 | else if (args[1].ToLower() == "-i") //安装服务
38 | {
39 | Install(true);
40 | return;
41 | }
42 | else if (args[1].ToLower() == "-u") //卸载服务
43 | {
44 | Install(false);
45 | return;
46 | }
47 | }
48 |
49 | Application.EnableVisualStyles();
50 | Application.SetCompatibleTextRenderingDefault(false);
51 | Application.Run(new FrmMain());
52 | }
53 |
54 | ///
55 | /// 安装、卸载 服务
56 | ///
57 | /// 是否安装
58 | public static void Install(Boolean isinstall)
59 | {
60 | var p = new Process();
61 | var si = new ProcessStartInfo();
62 | //String path = Environment.GetEnvironmentVariable("SystemRoot");
63 | //path = Path.Combine(path, @"Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe");
64 | var path = Environment.SystemDirectory;
65 | path = Path.Combine(path, @"sc.exe");
66 | if (!File.Exists(path)) path = "sc.exe";
67 | if (!File.Exists(path)) return;
68 | si.FileName = path;
69 | if (isinstall)
70 | si.Arguments = String.Format("create XProxy BinPath= \"{0} -s\" start= auto DisplayName= XProxy代理服务器", Application.ExecutablePath);
71 | else
72 | si.Arguments = @"Delete XProxy";
73 | si.UseShellExecute = false;
74 | si.CreateNoWindow = false;
75 | p.StartInfo = si;
76 | p.Start();
77 | p.WaitForExit();
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/XP/ProjectInstaller.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace XP
2 | {
3 | partial class ProjectInstaller
4 | {
5 | ///
6 | /// 必需的设计器变量。
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// 清理所有正在使用的资源。
12 | ///
13 | /// 如果应释放托管资源,为 true;否则为 false。
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region 组件设计器生成的代码
24 |
25 | ///
26 | /// 设计器支持所需的方法 - 不要
27 | /// 使用代码编辑器修改此方法的内容。
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
32 | this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
33 | //
34 | // serviceProcessInstaller1
35 | //
36 | this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
37 | this.serviceProcessInstaller1.Password = null;
38 | this.serviceProcessInstaller1.Username = null;
39 | //
40 | // serviceInstaller1
41 | //
42 | this.serviceInstaller1.Description = "新生命XProxy代理";
43 | this.serviceInstaller1.DisplayName = "XProxySvc";
44 | this.serviceInstaller1.ServiceName = "XProxySvc";
45 | this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
46 | //
47 | // ProjectInstaller
48 | //
49 | this.Installers.AddRange(new System.Configuration.Install.Installer[] {
50 | this.serviceProcessInstaller1,
51 | this.serviceInstaller1});
52 |
53 | }
54 |
55 | #endregion
56 |
57 | private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
58 | private System.ServiceProcess.ServiceInstaller serviceInstaller1;
59 | }
60 | }
--------------------------------------------------------------------------------
/XP/ProjectInstaller.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Configuration.Install;
5 |
6 | namespace XP
7 | {
8 | [RunInstaller(true)]
9 | public partial class ProjectInstaller : Installer
10 | {
11 | public ProjectInstaller()
12 | {
13 | InitializeComponent();
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/XP/ProjectInstaller.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 217, 17
125 |
126 |
127 | False
128 |
129 |
--------------------------------------------------------------------------------
/XP/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过下列属性集
6 | // 控制。更改这些属性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("新生命XProxy代理")]
9 | [assembly: AssemblyDescription("新生命XProxy代理")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("新生命开发团队(http://www.nnhy.org)")]
12 | [assembly: AssemblyProduct("新生命XProxy代理")]
13 | [assembly: AssemblyCopyright("版权所有 (C) NewLife 2008")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 属性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("5d5afc70-5fb8-40dd-87bd-8dd9a6c12121")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 内部版本号
30 | // 修订号
31 | //
32 | [assembly: AssemblyVersion("1.1.*")]
33 | [assembly: AssemblyFileVersion("1.1.2008.0307")]
34 |
--------------------------------------------------------------------------------
/XP/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace XP.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// 返回此类使用的缓存的 ResourceManager 实例。
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("XP.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 重写当前线程的 CurrentUICulture 属性
51 | /// 重写当前线程的 CurrentUICulture 属性。
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/XP/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/XP/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace XP.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/XP/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/XP/XP.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 | AnyCPU
5 | 9.0.21022
6 | 2.0
7 | {4D7D2273-F63B-498C-BD82-A4CB929A461B}
8 | WinExe
9 | Properties
10 | XP
11 | XP
12 | SAK
13 | SAK
14 | SAK
15 | SAK
16 | leaf.ico
17 |
18 |
19 | 3.5
20 |
21 |
22 | v4.6.1
23 |
24 |
25 |
26 | true
27 | full
28 | false
29 | ..\..\生成\
30 | DEBUG;TRACE
31 | prompt
32 | 4
33 | false
34 |
35 |
36 | pdbonly
37 | true
38 | ..\..\生成\
39 | TRACE
40 | prompt
41 | 4
42 | false
43 |
44 |
45 |
46 | ..\packages\NewLife.Core.7.3.6839.35284\lib\net45\NewLife.Core.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Form
61 |
62 |
63 | FrmMain.cs
64 |
65 |
66 | Form
67 |
68 |
69 | ListenerManage.cs
70 |
71 |
72 |
73 | Component
74 |
75 |
76 | ProjectInstaller.cs
77 |
78 |
79 |
80 | Designer
81 | FrmMain.cs
82 |
83 |
84 | Designer
85 | ListenerManage.cs
86 |
87 |
88 | Designer
89 | ProjectInstaller.cs
90 |
91 |
92 | ResXFileCodeGenerator
93 | Designer
94 | Resources.Designer.cs
95 |
96 |
97 |
98 |
99 | SettingsSingleFileGenerator
100 | Settings.Designer.cs
101 |
102 |
103 | True
104 | True
105 | Resources.resx
106 |
107 |
108 | True
109 | Settings.settings
110 | True
111 |
112 |
113 | Component
114 |
115 |
116 | XProxySvc.cs
117 |
118 |
119 |
120 |
121 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}
122 | XProxy
123 |
124 |
125 |
126 |
127 |
128 |
129 |
136 |
--------------------------------------------------------------------------------
/XP/XProxySvc.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace XP
2 | {
3 | partial class XProxySvc
4 | {
5 | ///
6 | /// 必需的设计器变量。
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// 清理所有正在使用的资源。
12 | ///
13 | /// 如果应释放托管资源,为 true;否则为 false。
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region 组件设计器生成的代码
24 |
25 | ///
26 | /// 设计器支持所需的方法 - 不要
27 | /// 使用代码编辑器修改此方法的内容。
28 | ///
29 | private void InitializeComponent()
30 | {
31 | components = new System.ComponentModel.Container();
32 | this.ServiceName = "XProxySvc";
33 | }
34 |
35 | #endregion
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/XP/XProxySvc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.ServiceProcess;
5 | using NewLife.Log;
6 | using XProxy.Base;
7 | using XProxy.Config;
8 |
9 | namespace XP
10 | {
11 | partial class XProxySvc : ServiceBase
12 | {
13 | public XProxySvc() => InitializeComponent();
14 |
15 | private WriteLogDelegate _WriteLogEvent;
16 | ///
17 | /// 写日志事件
18 | ///
19 | public WriteLogDelegate WriteLogEvent
20 | {
21 | get
22 | {
23 | if (_WriteLogEvent == null) return new WriteLogDelegate(XTrace.WriteLine);
24 | return _WriteLogEvent;
25 | }
26 | set { _WriteLogEvent = value; }
27 | }
28 |
29 | protected override void OnStart(String[] args)
30 | {
31 | // TODO: 在此处添加代码以启动服务。
32 | XTrace.WriteLine("服务启动!");
33 |
34 | StartService();
35 | }
36 |
37 | protected override void OnStop()
38 | {
39 | // TODO: 在此处添加代码以执行停止服务所需的关闭操作。
40 | XTrace.WriteLine("服务停止!");
41 |
42 | StopService();
43 | }
44 |
45 | ///
46 | /// 监听器集合
47 | ///
48 | public IList Listeners = new List();
49 |
50 | ///
51 | /// 启动服务
52 | ///
53 | public void StartService()
54 | {
55 | var config = LoadConfig();
56 | if (config == null)
57 | {
58 | XTrace.WriteLine("无法找到配置文件!");
59 | return;
60 | }
61 |
62 | foreach (var lc in config.Listeners)
63 | {
64 | if (lc.Enable)
65 | {
66 | try
67 | {
68 | var listener = new Listener(lc);
69 | Listeners.Add(listener);
70 | if (lc.IsShow) listener.OnWriteLog += WriteLogEvent;
71 | listener.Start();
72 | }
73 | catch (Exception ex)
74 | {
75 | XTrace.WriteLine("启动[" + lc.Name + "]时出错!" + ex.ToString());
76 | }
77 | }
78 | }
79 | }
80 |
81 | ///
82 | /// 停止服务
83 | ///
84 | public void StopService()
85 | {
86 | if (Listeners == null || Listeners.Count < 1) return;
87 |
88 | foreach (var listener in Listeners)
89 | {
90 | try
91 | {
92 | listener.Dispose();
93 | }
94 | catch (Exception ex)
95 | {
96 | XTrace.WriteLine(ex.ToString());
97 | }
98 | }
99 | }
100 |
101 | ///
102 | /// 配置文件监视
103 | ///
104 | public FileSystemWatcher watcher;
105 |
106 | ///
107 | /// 加载配置
108 | ///
109 | ///
110 | ProxyConfig LoadConfig()
111 | {
112 | if (!File.Exists(ProxyConfig.DefaultFile)) return null;
113 |
114 | var config = ProxyConfig.Instance;
115 | if (config == null || config.Listeners == null || config.Listeners.Length < 1) return null;
116 | //服务不会修改配置文件,所以不保存
117 | //config.IsSaved = true;
118 |
119 | //建立监视器
120 | if (watcher == null)
121 | {
122 | watcher = new FileSystemWatcher();
123 | var path = ProxyConfig.DefaultFile;
124 | watcher.Path = Path.GetDirectoryName(path);
125 | watcher.Filter = Path.GetFileName(path);
126 | watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
127 | watcher.Changed += new FileSystemEventHandler(watcher_Changed);
128 | watcher.EnableRaisingEvents = true;
129 | //XTrace.WriteLine("建立监视器!");
130 | }
131 | return config;
132 | }
133 |
134 | ///
135 | /// 配置文件修改时重启服务
136 | ///
137 | ///
138 | ///
139 | void watcher_Changed(Object sender, FileSystemEventArgs e)
140 | {
141 | XTrace.WriteLine("配置文件被修改!重新加载配置!");
142 | StopService();
143 | StartService();
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/XP/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/XP/leaf.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XP/leaf.ico
--------------------------------------------------------------------------------
/XP/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/XProxy.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31515.178
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XProxy", "XProxy\XProxy.csproj", "{E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Other", "Other", "{51A279CA-D58B-4E8C-89D3-89EC10873C14}"
9 | ProjectSection(SolutionItems) = preProject
10 | Readme.MD = Readme.MD
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{245A0359-8307-4C74-AD36-D36526704DF3}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {E2BA9F2D-AE8E-40AD-A18D-CB99AF3EE765}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {245A0359-8307-4C74-AD36-D36526704DF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {245A0359-8307-4C74-AD36-D36526704DF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {245A0359-8307-4C74-AD36-D36526704DF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {245A0359-8307-4C74-AD36-D36526704DF3}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {EF0B8BE3-C688-4539-99F3-3BCD7360ED9C}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/XProxy/Base/Connection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Net.Sockets;
5 | using System.Text;
6 | using System.Threading;
7 |
8 | namespace XProxy.Base
9 | {
10 | ///
11 | /// 网络连接。
12 | ///
13 | public class Connection : IDisposable
14 | {
15 | #region 属性
16 | #region 基本属性
17 | ///
18 | /// Tcp连接
19 | ///
20 | public TcpClient Tcp;
21 |
22 | ///
23 | /// 网络流
24 | ///
25 | private NetworkStream Stream
26 | {
27 | get
28 | {
29 | return Tcp == null ? null : Tcp.GetStream();
30 | }
31 | }
32 |
33 | ///
34 | /// 缓存数据
35 | ///
36 | private Byte[] Buffer = new Byte[1024 * 8];
37 | ///
38 | /// 编码
39 | ///
40 | public Encoding Encode { get; set; }
41 |
42 | private String _Name;
43 | ///
44 | /// 连接名
45 | ///
46 | public String Name
47 | {
48 | get { return _Name; }
49 | set { _Name = value; }
50 | }
51 | ///
52 | /// 会话
53 | ///
54 | public Session Session { get; set; }
55 | #endregion
56 |
57 | #region 便捷属性
58 | ///
59 | /// 基础Socket
60 | ///
61 | private Socket Socket
62 | {
63 | get { return Tcp == null ? null : Tcp.Client; }
64 | }
65 |
66 | ///
67 | /// 网络流是否可用
68 | ///
69 | public Boolean Active
70 | {
71 | get
72 | {
73 | return Socket != null && Socket.Connected;
74 | }
75 | }
76 | #endregion
77 | #endregion
78 |
79 | #region 事件
80 | ///
81 | /// 异步读取数据完成时触发
82 | ///
83 | public event ReadCompleteDelegate OnRead;
84 | #endregion
85 |
86 | #region 构造函数
87 | ///
88 | /// 构造函数
89 | ///
90 | ///
91 | public Connection(TcpClient tcpclient)
92 | {
93 | Tcp = tcpclient;
94 | Tcp.SendTimeout = 5000;
95 | Tcp.ReceiveTimeout = 5000;
96 | }
97 | #endregion
98 |
99 | #region IDisposable 成员
100 | private Boolean IsDisposed = false;
101 | ///
102 | /// 销毁网络连接占用的资源
103 | ///
104 | public void Dispose()
105 | {
106 | //双锁检查,防止多线程冲突
107 | if (IsDisposed) return;
108 | lock (this)
109 | {
110 | if (IsDisposed) return;
111 | IsDisposed = true;
112 | try
113 | {
114 | if (Stream != null) Stream.Close();
115 | if (Tcp != null) Tcp.Close();
116 | if (Buffer != null) Buffer = null;
117 | }
118 | catch (Exception ex)
119 | {
120 | Trace.WriteLine("关闭连接出错!" + ex.Message);
121 | }
122 |
123 | Session.Dispose();
124 | }
125 | }
126 |
127 | private void End(String msg)
128 | {
129 | WriteLog(msg);
130 | Dispose();
131 | }
132 |
133 | private void End(String format, params Object[] args)
134 | {
135 | End(String.Format(format, args));
136 | }
137 |
138 | private void End(String action, Exception ex)
139 | {
140 | End("{0}操作出错!{1}", action, ex.Message);
141 | }
142 |
143 | ///
144 | /// 终止处理
145 | ///
146 | ///
147 | private void End(Exception ex)
148 | {
149 | //取得调用本函数的方法名
150 | var st = new StackTrace(1, true);
151 | var sf = st.GetFrame(0);
152 | End(String.Format("{0}->{1}", sf.GetMethod().DeclaringType.FullName, sf.GetMethod().ToString()), ex);
153 | }
154 | #endregion
155 |
156 | #region 数据交换过程
157 | #region 异步读
158 | ///
159 | /// 开始异步读取
160 | ///
161 | public void BeginRead()
162 | {
163 | if (!Active) return;
164 |
165 | try
166 | {
167 | var iar = Stream.BeginRead(Buffer, 0, Buffer.Length, new AsyncCallback(EndRead), null);
168 | }
169 | catch (Exception ex)
170 | {
171 | End(ex);
172 | return;
173 | }
174 | }
175 |
176 | ///
177 | /// 处理异步读取的结束
178 | ///
179 | ///
180 | private void EndRead(IAsyncResult ar)
181 | {
182 | var Ret = 0;
183 | if (!Active) return;
184 |
185 | try
186 | {
187 | Ret = Stream.EndRead(ar);
188 | }
189 | catch (Exception ex)
190 | {
191 | End(ex);
192 | return;
193 | }
194 |
195 | if (Ret <= 0)
196 | {
197 | if (Ret == 0)
198 | End("收到空数据 断开");
199 | else
200 | End("读取数据出错");
201 | return;
202 | }
203 | //取出数据
204 | var buf = ByteHelper.SubBytes(Buffer, 0, Ret);
205 |
206 | //业务处理
207 | try
208 | {
209 | if (OnRead == null) End("必须指定异步读取完成事件!");
210 |
211 | buf = OnRead(buf);
212 | }
213 | catch (Exception ex)
214 | {
215 | WriteLog("EndRead业务处理出错!" + ex.Message);
216 | }
217 |
218 | // 重新建立委托
219 | BeginRead();
220 | }
221 | #endregion
222 |
223 | #region 异步写
224 | ///
225 | /// 开始异步写入
226 | ///
227 | public void BeginWrite(Byte[] buf)
228 | {
229 | if (buf == null || buf.Length < 1) return;
230 |
231 | if (!Active) return;
232 |
233 | try
234 | {
235 | var iar = Stream.BeginWrite(buf, 0, buf.Length, new AsyncCallback(EndWrite), null);
236 | CheckTimeOut(iar);
237 | }
238 | catch (Exception ex)
239 | {
240 | End(ex);
241 | return;
242 | }
243 | }
244 |
245 | ///
246 | /// 处理异步写入的结束
247 | ///
248 | ///
249 | private void EndWrite(IAsyncResult ar)
250 | {
251 | if (!Active) return;
252 |
253 | try
254 | {
255 | Stream.EndWrite(ar);
256 | }
257 | catch (Exception ex)
258 | {
259 | End(ex);
260 | return;
261 | }
262 | }
263 | #endregion
264 |
265 | #region 同步读
266 | ///
267 | /// 读取数据
268 | ///
269 | ///
270 | public Byte[] Read()
271 | {
272 | if (!Active) return null;
273 |
274 | var list = new List();
275 | var buf = new Byte[1024 * 8];
276 | try
277 | {
278 | //如果没数据,等100ms
279 | if (Socket.Available == 0 && Socket.Connected)
280 | {
281 | Socket.Poll(100000 /* 100ms */, SelectMode.SelectRead);
282 | //如果还是没数据,等10秒
283 | if (Socket.Available == 0 && Socket.Connected)
284 | {
285 | Socket.Poll(Session.TimeOut * 1000 /* 10sec */, SelectMode.SelectRead);
286 | //如果还是没有数据,退出
287 | if (Socket.Available == 0 && Socket.Connected) return null;
288 | }
289 | }
290 |
291 | do
292 | {
293 | //读数据
294 | var count = Stream.Read(buf, 0, buf.Length);
295 | if (count == 0) break;
296 | list.Add(new NetData(buf, 0, count));
297 | } while (Socket.Available != 0 && Socket.Connected);
298 | }
299 | catch { }
300 |
301 | if (list == null || list.Count < 1) return null;
302 | return NetData.Join(list);
303 | }
304 | #endregion
305 |
306 | #region 同步写
307 | ///
308 | /// 写入数据
309 | ///
310 | ///
311 | public void Write(Byte[] buf)
312 | {
313 | if (!Active) return;
314 |
315 | Stream.Write(buf, 0, buf.Length);
316 | }
317 | #endregion
318 | #endregion
319 |
320 | #region 辅助函数
321 | #region 异步超时检查
322 | ///
323 | /// 建立检查是否超时的委托
324 | ///
325 | ///
326 | private void CheckTimeOut(IAsyncResult iar)
327 | {
328 | ThreadPool.RegisterWaitForSingleObject(iar.AsyncWaitHandle, new WaitOrTimerCallback(TimeOutCallback), iar, Session.TimeOut, true);
329 | }
330 |
331 | ///
332 | /// 超时回调函数
333 | ///
334 | /// 一个对象,包含回调方法在每次执行时要使用的信息
335 | /// 如果 WaitHandle 超时,则为 true;如果其终止,则为 false
336 | private void TimeOutCallback(Object state, Boolean timedOut)
337 | {
338 | // 如果是因为超时而触发,不是因为对象被销毁而触发
339 | if (timedOut)
340 | {
341 | var ar = state as IAsyncResult;
342 | if (ar != null && !ar.IsCompleted)
343 | {
344 | End("超时退出");
345 | //Dispose();
346 | }
347 | }
348 | }
349 | #endregion
350 |
351 | #region 写日志
352 | ///输入日志
353 | ///输入日志信息到UI信息框
354 | ///要输出的日志信息
355 | public void WriteLog(String log)
356 | {
357 | Session.WriteLog(String.Format("{0} {1}", Name, log));
358 | }
359 | #endregion
360 | #endregion
361 | }
362 |
363 | ///
364 | /// 异步读取完成委托
365 | ///
366 | /// 数据
367 | public delegate Byte[] ReadCompleteDelegate(Byte[] buf);
368 | }
--------------------------------------------------------------------------------
/XProxy/Base/Listener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using NewLife.Log;
6 | using XProxy.Config;
7 | using XProxy.Plugin;
8 |
9 | namespace XProxy.Base
10 | {
11 | ///
12 | /// 代理监听器类
13 | ///
14 | public class Listener : IDisposable
15 | {
16 | #region 属性
17 | private IPAddress _Address;
18 | ///
19 | /// 地址
20 | ///
21 | public IPAddress Address
22 | {
23 | get
24 | {
25 | if (_Address != null) return _Address;
26 |
27 | if (Config.Address == "0.0.0.0")
28 | _Address = IPAddress.Any;
29 | else
30 | {
31 | try
32 | {
33 | _Address = Dns.GetHostEntry(Config.Address).AddressList[0];
34 | }
35 | catch (Exception ex)
36 | {
37 | Dispose();
38 | var msg = "获取[" + Config.Address + "]的IP地址时出错!";
39 | WriteLog(msg);
40 | XTrace.WriteLine(msg);
41 | throw ex;
42 | }
43 | }
44 | return _Address;
45 | }
46 | }
47 | ///
48 | /// Tcp监听器
49 | ///
50 | public TcpListener TcpServer { get; set; }
51 |
52 | ///
53 | /// 客户端数组。用来记录所有已连接客户端。
54 | ///
55 | public List Clients = new List();
56 |
57 | ///
58 | /// 写日志委托
59 | ///
60 | public event WriteLogDelegate OnWriteLog;
61 | ///
62 | /// 插件管理器
63 | ///
64 | internal PluginManager Plugin { get; set; }
65 |
66 | private ListenerConfig _Config;
67 | ///
68 | /// 配置
69 | ///
70 | public ListenerConfig Config
71 | {
72 | get
73 | {
74 | return _Config;
75 | }
76 | set
77 | {
78 | if (_Config != value)
79 | {
80 | _Config = value;
81 | _Address = null;
82 | }
83 | }
84 | }
85 | #endregion
86 |
87 | #region 构造函数
88 | ///
89 | /// 初始化监听器类的新实例
90 | ///
91 | ///
92 | public Listener(ListenerConfig config) => Config = config;
93 | #endregion
94 |
95 | #region 开始/停止
96 | ///
97 | /// 在指定的IP和端口上开始监听
98 | ///
99 | public virtual void Start()
100 | {
101 | if (Plugin == null)
102 | {
103 | Plugin = new PluginManager
104 | {
105 | Listener = this
106 | };
107 | if (Config.IsShow && OnWriteLog != null) Plugin.OnWriteLog += new WriteLogDelegate(WriteLog);
108 | Plugin.OnInit(this);
109 | }
110 |
111 | try
112 | {
113 | var plugincount = Plugin.Plugins == null ? 0 : Plugin.Plugins.Count;
114 | WriteLog(String.Format("开始监听 {0}:{1} [{2}] 插件:{3}个", Address.ToString(), Config.Port, Config.Name, plugincount));
115 | if (TcpServer != null) Stop();
116 | TcpServer = new TcpListener(Address, Config.Port)
117 | {
118 | // 指定 TcpListener 是否只允许一个基础套接字来侦听特定端口。
119 | // 只有监听任意IP的时候,才打开 ExclusiveAddressUse
120 | ExclusiveAddressUse = (Address == IPAddress.Any)
121 | };
122 | TcpServer.Start();
123 | // 开始异步接受传入的连接
124 | TcpServer.BeginAcceptTcpClient(new AsyncCallback(OnAccept), TcpServer);
125 | IsDisposed = false;
126 | Plugin.OnListenerStart(this);
127 | }
128 | catch (Exception ex)
129 | {
130 | Dispose();
131 | var msg = "开始监听" + Address.ToString() + ":" + Config.Port + "时出错!" + ex.Message;
132 | WriteLog(msg);
133 | XTrace.WriteLine(msg);
134 | throw ex;
135 | }
136 | }
137 |
138 | ///
139 | /// 停止监听
140 | ///
141 | public virtual void Stop()
142 | {
143 | WriteLog("停止监听" + Address.ToString() + ":" + Config.Port);
144 | if (Clients != null)
145 | {
146 | // Dispose时会关闭每个客户端的连接
147 | for (var i = Clients.Count - 1; i >= 0; i--)
148 | (Clients[i] as Session).Dispose();
149 | Clients.Clear();
150 | }
151 | try
152 | {
153 | TcpServer.Stop();
154 | if (TcpServer.Server != null)
155 | {
156 | if (TcpServer.Server.Connected)
157 | TcpServer.Server.Shutdown(SocketShutdown.Both);
158 | TcpServer.Server.Close();
159 | }
160 | Plugin.OnListenerStop(this);
161 | IsDisposed = true;
162 | }
163 | catch (Exception ex)
164 | {
165 | var msg = "停止监听" + Address.ToString() + ":" + Config.Port + "时出错!" + ex.Message;
166 | WriteLog(msg);
167 | XTrace.WriteLine(msg);
168 | throw ex;
169 | }
170 | }
171 | #endregion
172 |
173 | #region 新客户端到来
174 | ///
175 | /// 当客户链接等待传入时被调用。
176 | /// 关闭监听器时,也被调用,只不过EndAcceptTcpClient会触发异常
177 | ///
178 | ///异步操作的结果
179 | private void OnAccept(IAsyncResult ar)
180 | {
181 | TcpClient tcp = null;
182 | try
183 | {
184 | tcp = TcpServer.EndAcceptTcpClient(ar);
185 | }
186 | catch { return; }
187 | try
188 | {
189 | // 先重新开始监听,不要耽误了别的来访者访问
190 | TcpServer.BeginAcceptTcpClient(new AsyncCallback(OnAccept), TcpServer);
191 | }
192 | catch { Dispose(); }
193 | try
194 | {
195 | if (tcp != null)
196 | {
197 | NetHelper.SetKeepAlive(tcp.Client, true, 30000, 30000);
198 | var NewClient = OnAccept(tcp);
199 | if (Config.IsShow && OnWriteLog != null) NewClient.OnWriteLog += new WriteLogDelegate(WriteLog);
200 | NewClient.OnDestroy += new DestroyDelegate(ClientDestroy);
201 | NewClient.Listener = this;
202 | NewClient.WriteLog("新客户 (" + tcp.Client.RemoteEndPoint.ToString() + ")");
203 | Clients.Add(NewClient);
204 | NewClient.Start();
205 | }
206 | }
207 | catch (Exception ex)
208 | {
209 | XTrace.WriteLine("监听器接受连接时出错! " + ex.Message);
210 | }
211 | }
212 |
213 | private Boolean ClientDestroy(Session session)
214 | {
215 | session.WriteLog("客户(" + session.IPAndPort + ")" + "退出");
216 | //WriteLog("客户" + session.GUID + "(" + session.IPAndPort + ")" + "退出");
217 | //GC.Collect();
218 | return Clients.Remove(session);
219 | }
220 |
221 | ///
222 | /// 新客户端连接。请通过该方法建立相应的客户端实例对象。
223 | /// 比如:return new HttpClient(session, ServerPort, ServerAddress);
224 | ///
225 | /// 对应的TcpClient
226 | /// 客户端实例
227 | public virtual Session OnAccept(TcpClient tcp) => new Session(tcp, Config.ServerPort, Config.ServerAddress);
228 | #endregion
229 |
230 | #region 资源销毁
231 | ///
232 | /// 是否已经销毁
233 | ///
234 | private Boolean IsDisposed = false;
235 |
236 | ///销毁监听器占用的资源
237 | ///停止监听,并销毁 所有 客户对象,一旦销毁,对象将不再使用
238 | ///
239 | public void Dispose()
240 | {
241 | if (IsDisposed) return;
242 | lock (this)
243 | {
244 | if (IsDisposed) return;
245 | IsDisposed = true;
246 | Stop();
247 | //GC.Collect();
248 | }
249 | }
250 | ///终止监听器
251 | ///调用Dispose的析构函数
252 | ~Listener()
253 | {
254 | Dispose();
255 | }
256 | #endregion
257 |
258 | #region 日志
259 | ///
260 | /// 写日志
261 | ///
262 | /// 日志
263 | public void WriteLog(String msg)
264 | {
265 | if (Config.IsShow && OnWriteLog != null)
266 | {
267 | OnWriteLog(msg);
268 | }
269 | }
270 | #endregion
271 | }
272 | }
--------------------------------------------------------------------------------
/XProxy/ByteHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace XProxy
6 | {
7 | ///
8 | /// 字节辅助类
9 | ///
10 | public static class ByteHelper
11 | {
12 | ///
13 | /// 连接两个字节数组成为一个
14 | ///
15 | /// 字节数组1
16 | /// 字节数组2
17 | ///
18 | public static Byte[] Cat(Byte[] buf1, Byte[] buf2)
19 | {
20 | //if (buf1 == null || buf1.Length <= 0) return buf2;
21 | //if (buf2 == null || buf2.Length <= 0) return buf1;
22 | if (buf1 == null || buf1.Length <= 0) return Copy(buf2);
23 | if (buf2 == null || buf2.Length <= 0) return Copy(buf1);
24 |
25 | var buf = new Byte[buf1.Length + buf2.Length];
26 | //for (int i = 0; i < buf1.Length; i++)
27 | // buf[i] = buf1[i];
28 | //for (int i = 0; i < buf2.Length; i++)
29 | // buf[buf1.Length + i] = buf2[i];
30 | Buffer.BlockCopy(buf1, 0, buf, 0, buf1.Length);
31 | Buffer.BlockCopy(buf2, 0, buf, buf1.Length, buf2.Length);
32 | return buf;
33 | }
34 |
35 | ///
36 | /// 截取字节数组的某一部分,成为新的字节数组
37 | ///
38 | /// 要截取的字节数组
39 | /// 开始位置
40 | /// 字节个数
41 | ///
42 | public static Byte[] SubBytes(Byte[] buf, Int32 index, Int32 count)
43 | {
44 | if (buf == null || buf.Length < index) return null;
45 |
46 | if (count < 1 || count > buf.Length - index) count = buf.Length - index;
47 |
48 | //直接复制
49 | if (index == 0 && count == buf.Length) return Copy(buf);
50 |
51 | var b = new Byte[count];
52 | //for (int i = 0; i < count; i++)
53 | // b[i] = buf[index + i];
54 | Buffer.BlockCopy(buf, index, b, 0, b.Length);
55 | return b;
56 | }
57 |
58 | ///
59 | /// 复制
60 | ///
61 | ///
62 | ///
63 | public static Byte[] Copy(Byte[] buf)
64 | {
65 | if (buf == null || buf.Length < 1) return null;
66 |
67 | var bts = new Byte[buf.Length];
68 | Buffer.BlockCopy(buf, 0, bts, 0, bts.Length);
69 | return bts;
70 | }
71 |
72 | ///
73 | /// 字节数组是否以另一指定的字符串开始
74 | ///
75 | /// 字节数组
76 | /// 要查找的字符串
77 | ///
78 | public static Boolean StartWith(Byte[] bts, String str)
79 | {
80 | if (String.IsNullOrEmpty(str)) return false;
81 | return StartWith(bts, Encoding.ASCII.GetBytes(str));
82 | }
83 |
84 | ///
85 | /// 字节数组是否以另一指定的字节数组开始
86 | ///
87 | /// 字节数组
88 | /// 另一指定的字节数组
89 | ///
90 | public static Boolean StartWith(Byte[] bts1, Byte[] bts2)
91 | {
92 | if (bts1 == null || bts2 == null) return false;
93 | if (bts1.Length < bts2.Length) return false;
94 | for (var i = 0; i < bts2.Length; i++)
95 | {
96 | if (bts1[i] != bts2[i]) return false;
97 | }
98 | return true;
99 | }
100 |
101 | ///
102 | /// 在字节数组中查找指定字符串的位置
103 | ///
104 | /// 字节数组
105 | /// 要查找的字符串
106 | ///
107 | public static Int32 IndexOf(Byte[] bts, String str)
108 | {
109 | if (String.IsNullOrEmpty(str)) return -1;
110 | return IndexOf(bts, Encoding.ASCII.GetBytes(str));
111 | }
112 |
113 | ///
114 | /// 在字节数组中查找另一字节数组的位置
115 | ///
116 | /// 字节数组
117 | /// 另一字节数组
118 | /// 成功返回位置,失败返回-1
119 | public static Int32 IndexOf(Byte[] bts1, Byte[] bts2)
120 | {
121 | if (bts1 == null || bts2 == null) return -1;
122 |
123 | for (Int32 i = 0, j = 0; i < bts1.Length; i++, j++)
124 | {
125 | if (bts1[i] != bts2[j]) // 找到一个不等,把j复位
126 | j = -1;
127 | else if (j == bts2.Length - 1) // bts2比较完毕,表示找到,返回结果
128 | return i - j;
129 | }
130 | return -1;
131 | }
132 |
133 | ///
134 | /// 查找字节数组中是否包含指定字符串
135 | ///
136 | /// 字节数组
137 | /// 指定字符串
138 | ///
139 | public static Boolean Contains(Byte[] bts, String str)
140 | {
141 | if (String.IsNullOrEmpty(str)) return false;
142 | return Contains(bts, Encoding.ASCII.GetBytes(str));
143 | }
144 |
145 | ///
146 | /// 查找字节数组中是否包含指定另一字节数组
147 | ///
148 | /// 字节数组
149 | /// 另一字节数组
150 | ///
151 | public static Boolean Contains(Byte[] bts1, Byte[] bts2)
152 | {
153 | return IndexOf(bts1, bts2) >= 0;
154 | }
155 |
156 | ///
157 | /// 把字节数组的指定区域替换为指定字符串
158 | ///
159 | /// 字节数组
160 | /// 字节数组总字节数
161 | /// 开始位置
162 | /// 区域长度,0表示直接插入而不是替换
163 | /// 字符串
164 | ///
165 | public static Int32 Replace(ref Byte[] bts, Int32 count, Int32 start, Int32 length, String str)
166 | {
167 | if (String.IsNullOrEmpty(str))
168 | return Replace(ref bts, count, start, length, new Byte[0]);
169 | else
170 | return Replace(ref bts, count, start, length, Encoding.ASCII.GetBytes(str));
171 | }
172 |
173 | ///
174 | /// 把字节数组的指定区域替换为另一指定字节数组
175 | ///
176 | /// 字节数组
177 | /// 字节数组总字节数
178 | /// 开始位置
179 | /// 区域长度,0表示直接插入而不是替换
180 | /// 另一指定字节数组
181 | ///
182 | public static Int32 Replace(ref Byte[] bts, Int32 count, Int32 start, Int32 length, Byte[] bts2)
183 | {
184 | if (bts == null || start < 0 || length < 0 || start + length > count - 1) return count;
185 |
186 | // 另一指定字节数组为空,表明要直接把后面的字节数据前移
187 | if (bts2 == null || bts2.Length == 0)
188 | {
189 | //前面空间过大,前移
190 | for (var i = start; i < count; i++) bts[i] = bts[length + i];
191 | return count - length;
192 | }
193 |
194 | var off = 0;
195 | //为新字节数组挪出位置来
196 | if (length < bts2.Length)
197 | {
198 | //前面空间不够,后移。注意:后移需要从右往左移
199 | off = bts2.Length - length;
200 | for (var i = count - 1; i >= start + length; i--) bts[off + i] = bts[i];
201 | }
202 | else if (length > bts2.Length)
203 | {
204 | //前面空间过大,前移。注意:前移需要从左往右移
205 | off = length - bts2.Length;
206 | for (var i = start + length - off; i < count; i++) bts[i] = bts[off + i];
207 | }
208 | //如果另一指定字节数组刚好能放进指定区域,就不用移动后面部分了
209 | for (var i = 0; i < bts2.Length; i++) bts[start + i] = bts2[i];
210 |
211 | return count + off;
212 | }
213 |
214 | ///
215 | /// 加密解密
216 | ///
217 | /// 数据
218 | /// 字节个数
219 | /// 密码
220 | /// 加密后数据个数
221 | public static Int32 Encrypt(ref Byte[] Data, Int32 Count, String key)
222 | {
223 | var bts = Encoding.ASCII.GetBytes(key);
224 | for (Int32 i = 0, j = 0; i < Count; i++, j++)
225 | {
226 | if (j >= bts.Length) j = 0;
227 | Data[i] ^= bts[j];
228 | }
229 | return Count;
230 | }
231 | }
232 | }
--------------------------------------------------------------------------------
/XProxy/Config/ListenerConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.ComponentModel;
5 | using System.Windows.Forms;
6 | using System.IO;
7 | using System.Xml.Serialization;
8 | using System.Collections;
9 | using System.Diagnostics;
10 | using System.Reflection;
11 | using System.ComponentModel.Design;
12 | using System.Drawing.Design;
13 | using XProxy.Http;
14 |
15 | namespace XProxy.Config
16 | {
17 | ///
18 | /// 设置类
19 | ///
20 | [Serializable]
21 | public class ListenerConfig
22 | {
23 | #region 属性
24 | ///
25 | /// 监听器名
26 | ///
27 | [Category(" 名称"), DefaultValue("未命名"), Description("监听器名")]
28 | public String Name { get; set; } = "未命名";
29 |
30 | ///
31 | /// 激活
32 | ///
33 | [Category(" 名称"), DefaultValue(true), Description("激活")]
34 | public Boolean Enable { get; set; } = true;
35 |
36 | ///
37 | /// 监听地址
38 | ///
39 | [Category("基本属性"), DefaultValue("0.0.0.0"), Description("监听地址")]
40 | public String Address { get; set; } = "0.0.0.0";
41 |
42 | ///
43 | /// 监听端口
44 | ///
45 | [Category("基本属性"), DefaultValue(808), Description("监听端口")]
46 | public Int32 Port { get; set; } = 808;
47 |
48 | ///
49 | /// 远程服务器地址
50 | ///
51 | [Category("基本属性"), DefaultValue("127.0.0.1"), Description("远程服务器地址")]
52 | public String ServerAddress { get; set; } = "127.0.0.1";
53 |
54 | ///
55 | /// 远程服务器端口
56 | ///
57 | [Category("基本属性"), DefaultValue(80), Description("远程服务器端口")]
58 | public Int32 ServerPort { get; set; } = 80;
59 |
60 | ///
61 | /// 使用异步
62 | ///
63 | [Category("基本属性"), DefaultValue(true), Description("使用异步")]
64 | public Boolean IsAsync { get; set; } = true;
65 |
66 | ///
67 | /// 显示日志
68 | ///
69 | [Category("日志"), DefaultValue(false), Description("显示日志")]
70 | public Boolean IsShow { get; set; } = false;
71 |
72 | ///
73 | /// 客户端
74 | ///
75 | [Category("日志"), DefaultValue(false), Description("显示客户端日志")]
76 | public Boolean IsShowClientData { get; set; } = false;
77 |
78 | ///
79 | /// 服务端
80 | ///
81 | [Category("日志"), DefaultValue(false), Description("显示服务端日志")]
82 | public Boolean IsShowServerData { get; set; } = false;
83 |
84 | ///
85 | /// 插件集合
86 | ///
87 | [Editor(typeof(PluginsEditor), typeof(UITypeEditor))]
88 | [Category("插件"), Description("插件集合")]
89 | public PluginConfig[] Plugins { get; set; }
90 |
91 | private PluginConfig[] _HttpPlugins;
92 | ///
93 | /// Http插件
94 | ///
95 | [Editor(typeof(HttpPluginsEditor), typeof(UITypeEditor))]
96 | [Category("插件"), Description("Http插件")]
97 | public PluginConfig[] HttpPlugins
98 | {
99 | get { return _HttpPlugins; }
100 | set
101 | {
102 | _HttpPlugins = value;
103 | //检查Plugins中是否存在HttpPlugin插件,如果没有则加上
104 | if (Plugins != null && Plugins.Length > 0)
105 | foreach (var item in Plugins)
106 | {
107 | if (item.ClassName == typeof(HttpPlugin).Name ||
108 | item.ClassName == typeof(HttpPlugin).FullName)
109 | {
110 | return;
111 | }
112 | }
113 | var list = new PluginConfig[Plugins == null ? 1 : Plugins.Length + 1];
114 | var pc = new HttpPlugin().DefaultConfig;
115 | list[list.Length - 1] = pc;
116 | Plugins = list;
117 | }
118 | }
119 | #endregion
120 | }
121 | }
--------------------------------------------------------------------------------
/XProxy/Config/PluginConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.Design;
4 | using System.Text;
5 | using XProxy.Plugin;
6 |
7 | namespace XProxy.Config
8 | {
9 | ///
10 | /// 插件配置
11 | ///
12 | [Serializable]
13 | [Description("插件配置")]
14 | public class PluginConfig
15 | {
16 | ///
17 | /// 插件名
18 | ///
19 | [ReadOnly(true)]
20 | [Category("基本"), DefaultValue("未命名插件"), Description("插件名")]
21 | public String Name { get; set; } = "未命名";
22 |
23 | ///
24 | /// 插件作者
25 | ///
26 | [ReadOnly(true)]
27 | [Category("基本"), DefaultValue("无名"), Description("插件作者")]
28 | public String Author { get; set; }
29 |
30 | ///
31 | /// 插件版本
32 | ///
33 | [ReadOnly(true)]
34 | [Category("基本"), Description("插件版本")]
35 | public String Version { get; set; }
36 |
37 | ///
38 | /// 插件路径
39 | ///
40 | [ReadOnly(true)]
41 | //[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
42 | [Category("配置"), Description("插件路径")]
43 | public String Path { get; set; }
44 |
45 | ///
46 | /// 类名
47 | ///
48 | [ReadOnly(true)]
49 | [Category("配置"), Description("类名")]
50 | public String ClassName { get; set; }
51 |
52 | ///
53 | /// 扩展信息1
54 | ///
55 | [Category("扩展"), Description("扩展信息")]
56 | public String Extend { get; set; }
57 | ///
58 | /// 扩展信息2
59 | ///
60 | [Category("扩展"), Description("扩展信息二")]
61 | public String Extend2 { get; set; }
62 |
63 | ///
64 | /// 已重载
65 | ///
66 | ///
67 | public override String ToString()
68 | {
69 | var sb = new StringBuilder();
70 | sb.Append(Name);
71 | if (!String.IsNullOrEmpty(Author)) sb.AppendFormat("({0})", Author);
72 | if (!String.IsNullOrEmpty(ClassName)) sb.AppendFormat(",{0} V{1}", ClassName, Version);
73 | if (!String.IsNullOrEmpty(Path)) sb.AppendFormat(",{0}", Path);
74 | //sb.AppendFormat("{0}({1}),{2} {3},{4}", Name, Author, ClassName, Version, Path);
75 | return sb.ToString();
76 | }
77 | }
78 |
79 | ///
80 | /// 插件集合编辑器
81 | ///
82 | public class PluginsEditor : ArrayEditor
83 | {
84 | ///
85 | /// 已重载
86 | ///
87 | ///
88 | public PluginsEditor(Type type)
89 | : base(type)
90 | {
91 | }
92 |
93 | ///
94 | /// 已重载
95 | ///
96 | ///
97 | ///
98 | protected override Object CreateInstance(Type itemType)
99 | {
100 | var form = new PluginSelectorForm
101 | {
102 | Plugins = PluginManager.AllPlugins
103 | };
104 | form.ShowDialog();
105 | if (form.SelectedItem != null)
106 | {
107 | var pc = form.SelectedItem;
108 | form.Dispose();
109 | return pc;
110 | }
111 | form.Dispose();
112 | return null;
113 | }
114 | }
115 |
116 | ///
117 | /// Http插件集合编辑器
118 | ///
119 | public class HttpPluginsEditor : PluginsEditor
120 | {
121 | ///
122 | /// 已重载
123 | ///
124 | ///
125 | public HttpPluginsEditor(Type type)
126 | : base(type)
127 | {
128 | }
129 |
130 | ///
131 | /// 已重载
132 | ///
133 | ///
134 | ///
135 | protected override Object CreateInstance(Type itemType)
136 | {
137 | var form = new PluginSelectorForm
138 | {
139 | Plugins = PluginManager.AllHttpPlugins
140 | };
141 | form.ShowDialog();
142 | if (form.SelectedItem != null)
143 | {
144 | var pc = form.SelectedItem;
145 | form.Dispose();
146 | return pc;
147 | }
148 | form.Dispose();
149 | return null;
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/XProxy/Config/PluginSelectorForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace XProxy.Config
2 | {
3 | partial class PluginSelectorForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PluginSelectorForm));
32 | this.listView1 = new System.Windows.Forms.ListView();
33 | this.名称 = new System.Windows.Forms.ColumnHeader();
34 | this.作者 = new System.Windows.Forms.ColumnHeader();
35 | this.类名 = new System.Windows.Forms.ColumnHeader();
36 | this.版本 = new System.Windows.Forms.ColumnHeader();
37 | this.路径 = new System.Windows.Forms.ColumnHeader();
38 | this.SuspendLayout();
39 | //
40 | // listView1
41 | //
42 | this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
43 | this.名称,
44 | this.作者,
45 | this.类名,
46 | this.版本,
47 | this.路径});
48 | this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
49 | this.listView1.FullRowSelect = true;
50 | this.listView1.GridLines = true;
51 | this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
52 | this.listView1.Location = new System.Drawing.Point(0, 0);
53 | this.listView1.MultiSelect = false;
54 | this.listView1.Name = "listView1";
55 | this.listView1.Size = new System.Drawing.Size(616, 258);
56 | this.listView1.TabIndex = 0;
57 | this.listView1.UseCompatibleStateImageBehavior = false;
58 | this.listView1.View = System.Windows.Forms.View.Details;
59 | this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick);
60 | //
61 | // 名称
62 | //
63 | this.名称.Text = "名称";
64 | this.名称.Width = 80;
65 | //
66 | // 作者
67 | //
68 | this.作者.Text = "作者";
69 | //
70 | // 类名
71 | //
72 | this.类名.Text = "类名";
73 | this.类名.Width = 150;
74 | //
75 | // 版本
76 | //
77 | this.版本.Text = "版本";
78 | this.版本.Width = 120;
79 | //
80 | // 路径
81 | //
82 | this.路径.Text = "路径";
83 | this.路径.Width = 200;
84 | //
85 | // PluginSelectorForm
86 | //
87 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
88 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
89 | this.ClientSize = new System.Drawing.Size(616, 258);
90 | this.Controls.Add(this.listView1);
91 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
92 | this.Name = "PluginSelectorForm";
93 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
94 | this.Text = "插件选择窗体";
95 | this.Load += new System.EventHandler(this.PluginSelectorForm_Load);
96 | this.ResumeLayout(false);
97 |
98 | }
99 |
100 | #endregion
101 |
102 | private System.Windows.Forms.ListView listView1;
103 | private System.Windows.Forms.ColumnHeader 名称;
104 | private System.Windows.Forms.ColumnHeader 作者;
105 | private System.Windows.Forms.ColumnHeader 类名;
106 | private System.Windows.Forms.ColumnHeader 版本;
107 | private System.Windows.Forms.ColumnHeader 路径;
108 |
109 | }
110 | }
--------------------------------------------------------------------------------
/XProxy/Config/PluginSelectorForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Text;
7 | using System.Windows.Forms;
8 |
9 | namespace XProxy.Config
10 | {
11 | internal partial class PluginSelectorForm : Form
12 | {
13 | public PluginSelectorForm()
14 | {
15 | InitializeComponent();
16 | }
17 | ///
18 | /// 选择项
19 | ///
20 | public PluginConfig SelectedItem { get; set; }
21 |
22 | private PluginConfig[] _Plugins;
23 | ///
24 | /// 插件集合
25 | ///
26 | public PluginConfig[] Plugins { get { return _Plugins; } set { _Plugins = value; } }
27 |
28 | private void PluginSelectorForm_Load(Object sender, EventArgs e)
29 | {
30 | listView1.Items.Clear();
31 | if (Plugins == null || Plugins.Length < 1) return;
32 |
33 | foreach (var item in Plugins)
34 | {
35 | var lv = listView1.Items.Add(item.Name);
36 | lv.SubItems.Add(item.Author);
37 | lv.SubItems.Add(item.ClassName);
38 | lv.SubItems.Add(item.Version);
39 | lv.SubItems.Add(item.Path);
40 | lv.Tag = item;
41 | }
42 | }
43 |
44 | private void listView1_DoubleClick(Object sender, EventArgs e)
45 | {
46 | if (listView1.SelectedItems == null || listView1.SelectedItems.Count < 1) return;
47 | SelectedItem = listView1.SelectedItems[0].Tag as PluginConfig;
48 | Close();
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/XProxy/Config/ProxyConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Xml.Serialization;
5 | using System.IO;
6 | using NewLife.Log;
7 |
8 | namespace XProxy.Config
9 | {
10 | ///
11 | /// 代理配置
12 | ///
13 | [Serializable]
14 | public class ProxyConfig
15 | {
16 | #region 属性
17 | ///
18 | /// 监听器集合
19 | ///
20 | public ListenerConfig[] Listeners { get; set; }
21 | #endregion
22 |
23 | #region 构造函数
24 | //public static List list = new List();
25 | //public ProxyConfig()
26 | //{
27 | // list.Add(this);
28 | //}
29 |
30 | private static ProxyConfig _Instance;
31 | ///
32 | /// 默认实例
33 | ///
34 | public static ProxyConfig Instance
35 | {
36 | get
37 | {
38 | if (_Instance == null)
39 | {
40 | _Instance = Load(null);
41 | //if (_Instance != null) _Instance.IsSaved = false;
42 | }
43 | return _Instance;
44 | }
45 | set
46 | {
47 | _Instance = value;
48 | }
49 | }
50 |
51 | /////
52 | ///// BindSource默认会初始化一个Config类,然后才调用GetList方法。
53 | ///// 所以,应该是只有调用了Load的那个方法才能Save。
54 | /////
55 | //[NonSerialized]
56 | //private bool IsSaved = true;
57 |
58 | ///
59 | /// 析构时保存
60 | ///
61 | ~ProxyConfig()
62 | {
63 | //if (!IsSaved)
64 | //{
65 | // Save(null, this);
66 | // IsSaved = true;
67 | //}
68 | }
69 | #endregion
70 |
71 | #region 加载保存
72 | ///
73 | /// 加载
74 | ///
75 | /// 文件名
76 | ///
77 | public static ProxyConfig Load(String filename)
78 | {
79 | if (String.IsNullOrEmpty(filename)) filename = DefaultFile;
80 | var config = new ProxyConfig();
81 | if (File.Exists(filename))
82 | {
83 | using (var sr = new StreamReader(filename, Encoding.UTF8))
84 | {
85 | var xs = new XmlSerializer(typeof(ProxyConfig));
86 | try
87 | {
88 | config = xs.Deserialize(sr) as ProxyConfig;
89 | }
90 | catch (Exception ex)
91 | {
92 | XTrace.WriteLine("加载代理配置文件" + filename + "时发生错误!\n" + ex.ToString());
93 | }
94 | sr.Close();
95 | }
96 | }
97 | return config;
98 | }
99 |
100 | ///
101 | /// 保存
102 | ///
103 | /// 文件名
104 | /// 要保存的对象
105 | public static void Save(String filename, ProxyConfig config)
106 | {
107 | if (config == null) return;
108 | if (String.IsNullOrEmpty(filename)) filename = DefaultFile;
109 | using (var sw = new StreamWriter(filename, false, Encoding.UTF8))
110 | {
111 | try
112 | {
113 | var xs = new XmlSerializer(typeof(ProxyConfig));
114 | xs.Serialize(sw, config);
115 | }
116 | catch (Exception ex)
117 | {
118 | XTrace.WriteLine("保存代理配置文件" + filename + "时发生错误!\n" + ex.ToString());
119 | }
120 | sw.Close();
121 | }
122 | }
123 |
124 | ///
125 | /// 默认配置文件
126 | ///
127 | public static String DefaultFile => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Proxy.xml");
128 | #endregion
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/XProxy/Http/HttpBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NewLife.Collections;
4 | using NewLife.Data;
5 |
6 | namespace NewLife.Net.Http
7 | {
8 | /// Http请求响应基类
9 | public abstract class HttpBase
10 | {
11 | #region 属性
12 | /// 协议版本
13 | public String Version { get; set; } = "1.1";
14 |
15 | /// 内容长度
16 | public Int32 ContentLength { get; set; } = -1;
17 |
18 | /// 内容类型
19 | public String ContentType { get; set; }
20 |
21 | /// 请求或响应的主体部分
22 | public Packet Body { get; set; }
23 |
24 | /// 主体长度
25 | public Int32 BodyLength => Body == null ? 0 : Body.Total;
26 |
27 | /// 是否已完整。头部未指定长度,或指定长度后内容已满足
28 | public Boolean IsCompleted => ContentLength < 0 || ContentLength <= BodyLength;
29 |
30 | /// 头部集合
31 | public IDictionary Headers { get; set; } = new NullableDictionary(StringComparer.OrdinalIgnoreCase);
32 |
33 | /// 获取/设置 头部
34 | ///
35 | ///
36 | public String this[String key] { get => Headers[key] + ""; set => Headers[key] = value; }
37 | #endregion
38 |
39 | #region 解析
40 | /// 快速验证协议头,剔除非HTTP协议。仅排除,验证通过不一定就是HTTP协议
41 | ///
42 | ///
43 | public static Boolean FastValidHeader(Packet pk)
44 | {
45 | // 性能优化,Http头部第一行以请求谓语或响应版本开头,然后是一个空格。最长谓语Options/Connect,版本HTTP/1.1,不超过10个字符
46 | var p = pk.IndexOf(new[] { (Byte)' ' }, 10);
47 | if (p < 0) return false;
48 |
49 | return true;
50 | }
51 |
52 | private static readonly Byte[] NewLine = new[] { (Byte)'\r', (Byte)'\n', (Byte)'\r', (Byte)'\n' };
53 | /// 分析请求头
54 | ///
55 | ///
56 | public Boolean Parse(Packet pk)
57 | {
58 | if (!FastValidHeader(pk)) return false;
59 |
60 | var p = pk.IndexOf(NewLine);
61 | if (p < 0) return false;
62 |
63 | var str = pk.ReadBytes(0, p).ToStr();
64 |
65 | // 截取
66 | var lines = str.Split("\r\n");
67 | Body = pk.Slice(p + 4);
68 |
69 | // 分析头部
70 | for (var i = 1; i < lines.Length; i++)
71 | {
72 | var line = lines[i];
73 | p = line.IndexOf(':');
74 | if (p > 0) Headers[line.Substring(0, p)] = line.Substring(p + 1).Trim();
75 | }
76 |
77 | ContentLength = Headers["Content-Length"].ToInt(-1);
78 | ContentType = Headers["Content-Type"];
79 |
80 | // 分析第一行
81 | if (!OnParse(lines[0])) return false;
82 |
83 | return true;
84 | }
85 |
86 | /// 分析第一行
87 | ///
88 | protected abstract Boolean OnParse(String firstLine);
89 | #endregion
90 |
91 | #region 读写
92 | /// 创建请求响应包
93 | ///
94 | public virtual Packet Build()
95 | {
96 | var data = Body;
97 | var len = data != null ? data.Count : -1;
98 |
99 | var header = BuildHeader(len);
100 | var rs = new Packet(header.GetBytes())
101 | {
102 | Next = data
103 | };
104 |
105 | return rs;
106 | }
107 |
108 | /// 创建头部
109 | ///
110 | ///
111 | protected abstract String BuildHeader(Int32 length);
112 | #endregion
113 | }
114 | }
--------------------------------------------------------------------------------
/XProxy/Http/HttpCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using NewLife.Collections;
4 |
5 | namespace NewLife.Net.Http
6 | {
7 | /// Http缓存。以Url作为缓存键
8 | class HttpCache
9 | {
10 | #region 属性
11 | /// 过期时间。单位是秒,默认0秒,表示永不过期
12 | public Int32 Expriod { get; set; } = 600;
13 |
14 | private DictionaryCache _Items;
15 | /// 缓存项
16 | private DictionaryCache Items
17 | {
18 | get
19 | {
20 | if (_Items == null) _Items = new DictionaryCache(StringComparer.OrdinalIgnoreCase) { Expire = Expriod };
21 | return _Items;
22 | }
23 | set { _Items = value; }
24 | }
25 | #endregion
26 |
27 | #region 方法
28 | public HttpCacheItem GetItem(String url)
29 | {
30 | return Items[url];
31 | //HttpCacheItem item = null;
32 | //if (!Items.TryGetValue(url, out item)) return null;
33 | //lock (Items)
34 | //{
35 | // if (!Items.TryGetValue(url, out item)) return null;
36 |
37 | // // 移除过期
38 | // if (item.ExpiredTime < DateTime.Now)
39 | // {
40 | // Items.Remove(url);
41 | // item = null;
42 | // }
43 |
44 | // return item;
45 | //}
46 | }
47 |
48 | public HttpCacheItem Add(HttpHeader request, HttpHeader response)
49 | {
50 | var url = request.RawUrl;
51 | var item = new HttpCacheItem() { Url = url, Request = request, Response = response };
52 | item.Stream = response.GetStream();
53 | //lock (Items)
54 | //{
55 | // Items[url] = item;
56 | //}
57 | Items[url] = item;
58 |
59 | return item;
60 | }
61 | #endregion
62 | }
63 |
64 | /// Http缓存项。
65 | class HttpCacheItem
66 | {
67 | #region 属性
68 | /// 网址
69 | public String Url { get; set; }
70 |
71 | /// 请求
72 | public HttpHeader Request { get; set; }
73 |
74 | /// 响应
75 | public HttpHeader Response { get; set; }
76 |
77 | private Stream _Stream;
78 | /// 数据流
79 | public Stream Stream { get { return _Stream ??= new MemoryStream(); } set { _Stream = value; } }
80 |
81 | //private DateTime _StartTime = DateTime.Now;
82 | ///// 开始时间
83 | //public DateTime StartTime { get { return _StartTime; } set { _StartTime = value; } }
84 |
85 | //private DateTime _ExpiredTime = DateTime.Now.AddMinutes(10);
86 | ///// 到期时间
87 | //public DateTime ExpiredTime { get { return _ExpiredTime; } set { _ExpiredTime = value; } }
88 | #endregion
89 | }
90 | }
--------------------------------------------------------------------------------
/XProxy/Http/HttpHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 | using System.Net;
6 | using XProxy.Http;
7 | using XProxy.Http.Plugin;
8 | using XProxy.Base;
9 |
10 | namespace XProxy
11 | {
12 | ///
13 | /// Http辅助类
14 | ///
15 | public static class HttpHelper
16 | {
17 | ///
18 | /// 分析HTTP头,包括请求头和相应头
19 | ///
20 | /// HTTP数据包
21 | ///
22 | public static String[] ParseHeader(Byte[] bts)
23 | {
24 | var p = ByteHelper.IndexOf(bts, "\r\n\r\n");
25 | if (p < 0) return null;
26 | return Encoding.ASCII.GetString(ByteHelper.SubBytes(bts, 0, p)).Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
27 | }
28 |
29 | ///
30 | /// 是否HTTP请求
31 | ///
32 | /// HTTP数据包
33 | ///
34 | public static Boolean IsHttpRequest(Byte[] bts)
35 | {
36 | var key = "GET ";
37 | if (bts == null || bts.Length < key.Length) return false;
38 | if (ByteHelper.StartWith(bts, key)) return true;
39 | if (ByteHelper.StartWith(bts, "POST ")) return true;
40 | return false;
41 | }
42 |
43 | ///
44 | /// 是否HTTP响应
45 | ///
46 | /// HTTP数据包
47 | ///
48 | public static Boolean IsHttpResponse(Byte[] bts)
49 | {
50 | var key = "HTTP/1.";
51 | if (bts == null || bts.Length < key.Length) return false;
52 | if (ByteHelper.StartWith(bts, key)) return true;
53 | return false;
54 | }
55 |
56 | ///
57 | /// 处理HTTP请求头。供HTTP间接代理使用
58 | ///
59 | /// 客户端
60 | /// 数据
61 | /// 处理后的数据
62 | public static String ProcessHttpRequestHeader(Session session, String header)
63 | {
64 | if (String.IsNullOrEmpty(header)) return header;
65 |
66 | // 找到HTTP头,尝试修正请求地址和主机HOST
67 | var headers = header.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
68 | if (headers == null) return header;
69 |
70 | var uri = GetUriFromHeader(header);
71 | session.ServerPort = 80;
72 | session.ServerPort = uri.Port;
73 | try
74 | {
75 | session.ServerAddress = Dns.GetHostEntry(uri.Host).AddressList[0];
76 | }
77 | catch (Exception ex)
78 | {
79 | session.WriteLog("无法解析主机地址 " + ex.Message);
80 | return null;
81 | }
82 |
83 | // 重新拼接HTTP请求头
84 | var sb = new StringBuilder();
85 | foreach (var s in headers)
86 | {
87 | var ss = s.ToLower();
88 | if (ss.StartsWith("get "))
89 | {
90 | sb.Append("GET ");
91 | sb.Append(uri.PathAndQuery);
92 | sb.Append(" HTTP/1.1");
93 | }
94 | else if (ss.StartsWith("post "))
95 | {
96 | sb.Append("POST ");
97 | sb.Append(uri.PathAndQuery);
98 | sb.Append(" HTTP/1.1");
99 | }
100 | else
101 | {
102 | sb.Append(s);
103 | }
104 | sb.Append("\r\n");
105 | }
106 | // 加上自己的标识
107 | sb.Append("X-Proxy: NewLifeXProxy");
108 |
109 | return sb.ToString();
110 | }
111 |
112 | ///
113 | /// 从请求头中取得Uri
114 | ///
115 | ///
116 | ///
117 | public static Uri GetUriFromHeader(String header)
118 | {
119 | if (String.IsNullOrEmpty(header)) return null;
120 |
121 | if (header.IndexOf("\r\n") < 0) return null;
122 | header = header.Substring(0, header.IndexOf("\r\n"));
123 |
124 | var i = header.IndexOf(" ");
125 | if (i < 0) return null;
126 | var j = header.IndexOf(" ", i + 1);
127 | if (j < 0) return null;
128 | return new Uri(header.Substring(i + 1, j - i));
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/XProxy/Http/HttpPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using XProxy.Plugin;
5 | using XProxy.Config;
6 | using System.Reflection;
7 | using System.IO;
8 | using XProxy.Base;
9 |
10 | namespace XProxy.Http
11 | {
12 | ///
13 | /// Http插件类
14 | ///
15 | public class HttpPlugin : PluginBase
16 | {
17 | #region 属性
18 | ///
19 | /// 请求延迟。
20 | /// 收到一个完整的请求后,才转发,否则直接转发。
21 | /// 如果要处理整个请求包,请启用延迟。
22 | /// 启用延迟将需要很大的内存资源来缓存数据。
23 | ///
24 | public Boolean RequestDelay { get; set; } = false;
25 |
26 | ///
27 | /// 响应延迟。
28 | /// 收到一个完整的响应后,才转发,否则直接转发。
29 | /// 如果要处理整个响应包,请启用延迟。
30 | /// 启用延迟将需要很大的内存资源来缓存数据。
31 | ///
32 | public Boolean ResponseDelay { get; set; } = false;
33 | ///
34 | /// 插件集合。处理各事件的时候,将按照先后顺序调用插件的处理方法。
35 | ///
36 | public IList Plugins { get; set; }
37 |
38 | ///
39 | /// 显示请求
40 | ///
41 | public Boolean ShowRequest { get; set; }
42 | ///
43 | /// 显示响应
44 | ///
45 | public Boolean ShowResponse { get; set; }
46 | #endregion
47 |
48 | #region 加载插件
49 | ///
50 | /// 加载插件
51 | ///
52 | ///
53 | private void LoadPlugin(IList configs)
54 | {
55 | if (configs == null || configs.Count < 1) return;
56 | var list = new List();
57 | foreach (var config in configs)
58 | {
59 | if (!String.IsNullOrEmpty(config.ClassName))
60 | {
61 | Assembly asm;
62 | if (String.IsNullOrEmpty(config.Path))
63 | asm = Assembly.GetExecutingAssembly();
64 | else
65 | {
66 | var path = config.Path;
67 | if (!Path.IsPathRooted(path)) path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
68 | try
69 | {
70 | asm = Assembly.LoadFile(path);
71 | }
72 | catch (Exception ex)
73 | {
74 | WriteLog(String.Format("加载Http插件出错:{0}\n{1}", config, ex));
75 | continue;
76 | }
77 | }
78 | if (asm != null)
79 | {
80 | var t = asm.GetType(config.ClassName, false);
81 | if (t == null)
82 | {
83 | var ts = asm.GetTypes();
84 | foreach (var tt in ts)
85 | {
86 | if (tt.Name.Contains(config.ClassName))
87 | {
88 | t = tt;
89 | break;
90 | }
91 | }
92 | }
93 | if (t != null)
94 | {
95 | var obj = Activator.CreateInstance(t);
96 | if (obj != null)
97 | {
98 | var p = obj as IHttpPlugin;
99 | if (p != null)
100 | {
101 | p.Config = config;
102 | list.Add(p);
103 | WriteLog(String.Format("加载Http插件:{0}", config));
104 | }
105 | }
106 | }
107 | }
108 | }
109 | }
110 | Plugins = list;
111 | }
112 | #endregion
113 |
114 | #region IPlugin 成员
115 | ///
116 | /// 初始化
117 | ///
118 | /// 插件管理器
119 | public override void OnInit(PluginManager manager)
120 | {
121 | Manager = manager;
122 |
123 | LoadPlugin(manager.Listener.Config.HttpPlugins);
124 | if (Plugins == null || Plugins.Count < 1) return;
125 |
126 | for (var i = 0; i < Plugins.Count; i++)
127 | {
128 | Plugins[i].OnWriteLog += new WriteLogDelegate(WriteLog);
129 | Plugins[i].OnInit(this);
130 | }
131 |
132 | //不让核心显示客户端和服务器数据,由这里来显示
133 | ShowRequest = manager.Listener.Config.IsShow && manager.Listener.Config.IsShowClientData;
134 | ShowResponse = manager.Listener.Config.IsShow && manager.Listener.Config.IsShowServerData;
135 | manager.Listener.Config.IsShowClientData = false;
136 | manager.Listener.Config.IsShowServerData = false;
137 | }
138 |
139 | public override Boolean OnClientStart(Session session)
140 | {
141 | //使用同步
142 | //session.IsAsync = false;
143 |
144 | return base.OnClientStart(session);
145 | }
146 |
147 | ///
148 | /// 客户端向服务器发数据时触发。
149 | ///
150 | /// 客户端
151 | /// 数据
152 | /// 经过处理后的数据
153 | public override Byte[] OnClientToServer(Session session, Byte[] Data)
154 | {
155 | if (Plugins == null || Plugins.Count < 1) return Data;
156 |
157 | //一般的请求都是ASCII编码,可以直接显示
158 | if (ShowRequest)
159 | session.WriteLog("请求头(" + Data.Length + "Byte):\n" + Encoding.ASCII.GetString(Data));
160 |
161 | if (HttpHelper.IsHttpRequest(Data))
162 | {
163 | var p = ByteHelper.IndexOf(Data, "\r\n\r\n");
164 | if (p < 0) return null;
165 | var bts = ByteHelper.SubBytes(Data, 0, p);
166 |
167 | for (var i = 0; i < Plugins.Count; i++)
168 | {
169 | bts = Plugins[i].OnRequestHeader(session, bts);
170 | //直接阻止
171 | if (bts == null || bts.Length < 1) return null;
172 | }
173 |
174 | //String header = Encoding.ASCII.GetString(bts);
175 |
176 | //for (int i = 0; i < Plugins.Count; i++)
177 | //{
178 | // header = Plugins[i].OnRequestHeader(session, header);
179 | // //直接阻止
180 | // if (String.IsNullOrEmpty(header)) return null;
181 | //}
182 |
183 | //bts = Encoding.ASCII.GetBytes(header + "\r\n\r\n");
184 |
185 | bts = ByteHelper.Cat(bts, Encoding.ASCII.GetBytes("\r\n\r\n"));
186 |
187 | //是否有数据需要处理
188 | if (Data.Length > p + 4)
189 | {
190 | Data = ByteHelper.SubBytes(Data, p + 4, -1);
191 | for (var i = 0; i < Plugins.Count; i++)
192 | {
193 | Data = Plugins[i].OnRequestContent(session, Data);
194 | //直接阻止
195 | if (Data == null || Data.Length < 1) return null;
196 | }
197 | Data = ByteHelper.Cat(bts, Data);
198 | }
199 | else
200 | Data = bts;
201 | }
202 | else
203 | {
204 | for (var i = 0; i < Plugins.Count; i++)
205 | {
206 | Data = Plugins[i].OnRequestContent(session, Data);
207 | //直接阻止
208 | if (Data == null || Data.Length < 1) return null;
209 | }
210 | }
211 |
212 | return Data;
213 | }
214 |
215 | ///
216 | /// 服务器相客户端发数据时触发。
217 | ///
218 | /// 客户端
219 | /// 数据
220 | /// 经过处理后的数据
221 | public override Byte[] OnServerToClient(Session session, Byte[] Data)
222 | {
223 | if (Plugins == null || Plugins.Count < 1) return Data;
224 |
225 | if (HttpHelper.IsHttpResponse(Data))
226 | {
227 | var p = ByteHelper.IndexOf(Data, "\r\n\r\n");
228 | if (p < 0) return null;
229 | var bts = ByteHelper.SubBytes(Data, 0, p);
230 |
231 | for (var i = 0; i < Plugins.Count; i++)
232 | {
233 | bts = Plugins[i].OnResponseHeader(session, bts);
234 | //直接阻止
235 | if (bts == null || bts.Length < 1) return null;
236 | }
237 |
238 | var header = Encoding.ASCII.GetString(bts);
239 |
240 | //对于响应,只能直接显示头部,只有头部可以用ASCII解码
241 | if (ShowResponse)
242 | session.WriteLog("响应头(" + Data.Length + "Byte):\n" + header);
243 |
244 | //for (int i = 0; i < Plugins.Count; i++)
245 | //{
246 | // header = Plugins[i].OnResponseHeader(session, header);
247 | // //直接阻止
248 | // if (String.IsNullOrEmpty(header)) return null;
249 | //}
250 |
251 | //bts = Encoding.ASCII.GetBytes(header + "\r\n\r\n");
252 |
253 | bts = ByteHelper.Cat(bts, Encoding.ASCII.GetBytes("\r\n\r\n"));
254 |
255 | //是否有数据需要处理
256 | if (Data.Length > p + 4)
257 | {
258 | Data = ByteHelper.SubBytes(Data, p + 4, -1);
259 | for (var i = 0; i < Plugins.Count; i++)
260 | {
261 | Data = Plugins[i].OnResponseContent(session, Data);
262 | //直接阻止
263 | if (Data == null || Data.Length < 1) return null;
264 | }
265 | Data = ByteHelper.Cat(bts, Data);
266 | }
267 | else
268 | Data = bts;
269 | }
270 | else
271 | {
272 | if (ShowResponse)
273 | {
274 | #if !DEBUG
275 | session.WriteLog("响应数据(" + Data.Length + "Byte)");
276 | #else
277 | session.WriteLog("响应数据(" + Data.Length + "Byte):\n" + Encoding.Default.GetString(Data));
278 | #endif
279 | }
280 | for (var i = 0; i < Plugins.Count; i++)
281 | {
282 | Data = Plugins[i].OnResponseContent(session, Data);
283 | //直接阻止
284 | if (Data == null || Data.Length < 1) return null;
285 | }
286 | }
287 |
288 | return Data;
289 | }
290 |
291 | ///
292 | /// 默认设置
293 | ///
294 | public override PluginConfig DefaultConfig
295 | {
296 | get
297 | {
298 | var pc = base.DefaultConfig;
299 | pc.Name = "Http插件";
300 | pc.Author = "大石头";
301 | return pc;
302 | }
303 | }
304 |
305 | #endregion
306 |
307 | #region IDisposable 成员
308 | ///
309 | /// 释放资源
310 | ///
311 | public override void Dispose()
312 | {
313 | if (Plugins == null || Plugins.Count < 1) return;
314 |
315 | for (var i = 0; i < Plugins.Count; i++)
316 | {
317 | Plugins[i].Dispose();
318 | }
319 | }
320 |
321 | #endregion
322 | }
323 | }
--------------------------------------------------------------------------------
/XProxy/Http/HttpPluginBase.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Http/HttpPluginBase.cs
--------------------------------------------------------------------------------
/XProxy/Http/HttpRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NewLife.Collections;
3 | using NewLife.Data;
4 |
5 | namespace NewLife.Net.Http
6 | {
7 | /// Http请求
8 | public class HttpRequest : HttpBase
9 | {
10 | #region 属性
11 | /// Http方法
12 | public String Method { get; set; }
13 |
14 | /// 资源路径
15 | public Uri Url { get; set; }
16 |
17 | /// 目标主机
18 | public String Host { get; set; }
19 |
20 | /// 保持连接
21 | public Boolean KeepAlive { get; set; }
22 | #endregion
23 |
24 | /// 分析第一行
25 | ///
26 | protected override Boolean OnParse(String firstLine)
27 | {
28 | if (firstLine.IsNullOrEmpty()) return false;
29 |
30 | var ss = firstLine.Split(' ');
31 | if (ss.Length < 3) return false;
32 |
33 | // 分析请求方法 GET / HTTP/1.1
34 | if (ss.Length >= 3 && ss[2].StartsWithIgnoreCase("HTTP/"))
35 | {
36 | Method = ss[0];
37 | Url = new Uri(ss[1], UriKind.RelativeOrAbsolute);
38 | Version = ss[2].TrimStart("HTTP/");
39 | }
40 |
41 | Host = Headers["Host"];
42 | KeepAlive = Headers["Connection"].EqualIgnoreCase("keep-alive");
43 |
44 | return true;
45 | }
46 |
47 | private static readonly Byte[] NewLine = new[] { (Byte)'\r', (Byte)'\n' };
48 | /// 快速分析请求头,只分析第一行
49 | ///
50 | ///
51 | public Boolean FastParse(Packet pk)
52 | {
53 | if (!FastValidHeader(pk)) return false;
54 |
55 | var p = pk.IndexOf(NewLine);
56 | if (p < 0) return false;
57 |
58 | var line = pk.ReadBytes(0, p).ToStr();
59 |
60 | Body = pk.Slice(p + 2);
61 |
62 | // 分析第一行
63 | if (!OnParse(line)) return false;
64 |
65 | return true;
66 | }
67 |
68 | /// 创建头部
69 | ///
70 | ///
71 | protected override String BuildHeader(Int32 length)
72 | {
73 | if (Method.IsNullOrEmpty()) Method = length > 0 ? "POST" : "GET";
74 |
75 | // 分解主机和资源
76 | var uri = Url;
77 | if (uri == null) uri = new Uri("/");
78 |
79 | if (Host.IsNullOrEmpty())
80 | {
81 | var host = "";
82 | if (uri.Scheme.EqualIgnoreCase("http", "ws"))
83 | {
84 | if (uri.Port == 80)
85 | host = uri.Host;
86 | else
87 | host = $"{uri.Host}:{uri.Port}";
88 | }
89 | else if (uri.Scheme.EqualIgnoreCase("https", "wss"))
90 | {
91 | if (uri.Port == 443)
92 | host = uri.Host;
93 | else
94 | host = $"{uri.Host}:{uri.Port}";
95 | }
96 | Host = host;
97 | }
98 |
99 | // 构建头部
100 | var sb = Pool.StringBuilder.Get();
101 | sb.AppendFormat("{0} {1} HTTP/{2}\r\n", Method, uri, Version);
102 | sb.AppendFormat("Host:{0}\r\n", Host);
103 |
104 | // 内容长度
105 | if (length > 0) Headers["Content-Length"] = length + "";
106 | if (!ContentType.IsNullOrEmpty()) Headers["Content-Type"] = ContentType;
107 |
108 | if (KeepAlive) Headers["Connection"] = "keep-alive";
109 |
110 | foreach (var item in Headers)
111 | {
112 | if (!item.Key.EqualIgnoreCase("Host"))
113 | sb.AppendFormat("{0}:{1}\r\n", item.Key, item.Value);
114 | }
115 |
116 | sb.AppendLine();
117 |
118 | return sb.Put(true);
119 | }
120 |
121 | /// 已重载。
122 | ///
123 | public override String ToString() => $"{Method} {Url}";
124 | }
125 | }
--------------------------------------------------------------------------------
/XProxy/Http/HttpResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Net;
5 | using NewLife.Collections;
6 | using NewLife.Data;
7 | using NewLife.Remoting;
8 | using NewLife.Serialization;
9 |
10 | namespace NewLife.Net.Http
11 | {
12 | /// Http响应
13 | public class HttpResponse : HttpBase
14 | {
15 | #region 属性
16 | /// 状态码
17 | public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;
18 |
19 | /// 状态描述
20 | public String StatusDescription { get; set; }
21 | #endregion
22 |
23 | /// 分析第一行
24 | ///
25 | protected override Boolean OnParse(String firstLine)
26 | {
27 | if (firstLine.IsNullOrEmpty()) return false;
28 |
29 | // HTTP/1.1 502 Bad Gateway
30 | if (!firstLine.StartsWith("HTTP/")) return false;
31 |
32 | var ss = firstLine.Split(" ");
33 | //if (ss.Length < 3) throw new Exception("非法响应头 {0}".F(firstLine));
34 | if (ss.Length < 3) return false;
35 |
36 | Version = ss[0].TrimStart("HTTP/");
37 |
38 | // 分析响应码
39 | var code = ss[1].ToInt();
40 | if (code > 0) StatusCode = (HttpStatusCode)code;
41 |
42 | StatusDescription = ss.Skip(2).Join(" ");
43 |
44 | return true;
45 | }
46 |
47 | /// 创建请求响应包
48 | ///
49 | public override Packet Build()
50 | {
51 | // 如果响应异常,则使用响应描述作为内容
52 | if (StatusCode > HttpStatusCode.OK && Body == null && !StatusDescription.IsNullOrEmpty())
53 | {
54 | Body = StatusDescription.GetBytes();
55 | }
56 |
57 | return base.Build();
58 | }
59 |
60 | /// 创建头部
61 | ///
62 | ///
63 | protected override String BuildHeader(Int32 length)
64 | {
65 | // 构建头部
66 | var sb = Pool.StringBuilder.Get();
67 | sb.AppendFormat("HTTP/{2} {0} {1}\r\n", (Int32)StatusCode, StatusCode, Version);
68 |
69 | //// cors
70 | //sb.AppendFormat("Access-Control-Allow-Origin:{0}\r\n", "*");
71 | //sb.AppendFormat("Access-Control-Allow-Methods:{0}\r\n", "POST, GET");
72 | //sb.AppendFormat("Access-Control-Allow-Headers:{0}\r\n", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
73 |
74 | // 内容长度
75 | if (length > 0)
76 | Headers["Content-Length"] = length + "";
77 | else if (!Headers.ContainsKey("Transfer-Encoding"))
78 | Headers["Content-Length"] = "0";
79 |
80 | if (!ContentType.IsNullOrEmpty()) Headers["Content-Type"] = ContentType;
81 |
82 | foreach (var item in Headers)
83 | {
84 | sb.AppendFormat("{0}:{1}\r\n", item.Key, item.Value);
85 | }
86 |
87 | sb.AppendLine();
88 |
89 | return sb.Put(true);
90 | }
91 |
92 | /// 验证,如果失败则抛出异常
93 | public void Valid()
94 | {
95 | if (StatusCode != HttpStatusCode.OK) throw new Exception(StatusDescription ?? (StatusCode + ""));
96 | }
97 |
98 | /// 设置结果,影响Body和ContentType
99 | ///
100 | ///
101 | public void SetResult(Object result, String contentType = null)
102 | {
103 | if (result == null) return;
104 |
105 | if (result is Exception ex)
106 | {
107 | if (ex is ApiException aex)
108 | StatusCode = (HttpStatusCode)aex.Code;
109 | else
110 | StatusCode = HttpStatusCode.InternalServerError;
111 |
112 | StatusDescription = ex.Message;
113 | }
114 | else if (result is Packet pk)
115 | {
116 | ContentType = contentType ?? "application/octet-stream";
117 | Body = pk;
118 | }
119 | else if (result is Byte[] buffer)
120 | {
121 | ContentType = contentType ?? "application/octet-stream";
122 | Body = buffer;
123 | }
124 | else if (result is Stream stream)
125 | {
126 | ContentType = contentType ?? "application/octet-stream";
127 | Body = stream.ReadBytes();
128 | }
129 | else if (result is String str)
130 | {
131 | ContentType = contentType ?? "text/html";
132 | Body = str.GetBytes();
133 | }
134 | else
135 | {
136 | ContentType = contentType ?? "application/json";
137 | Body = result.ToJson().GetBytes();
138 | }
139 | }
140 |
141 | /// 已重载。
142 | ///
143 | public override String ToString() => $"HTTP/{Version} {(Int32)StatusCode} {StatusDescription ?? (StatusCode + "")}";
144 | }
145 | }
--------------------------------------------------------------------------------
/XProxy/Http/IHttpPlugin.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Http/IHttpPlugin.cs
--------------------------------------------------------------------------------
/XProxy/Http/Plugin/Direct.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Http/Plugin/Direct.cs
--------------------------------------------------------------------------------
/XProxy/Http/Plugin/HttpCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using XProxy.Config;
5 | using System.Text.RegularExpressions;
6 | using System.IO;
7 | using System.Net;
8 | using XProxy.Base;
9 |
10 | namespace XProxy.Http.Plugin
11 | {
12 | ///
13 | /// 缓存插件
14 | ///
15 | public class HttpCache : HttpPluginBase
16 | {
17 | #region IHttpPlugin 成员
18 | #region 请求头/响应头 处理
19 | ///
20 | /// 请求头
21 | ///
22 | /// 客户端
23 | /// 请求头
24 | /// 处理后的请求头
25 | public override String OnRequestHeader(Session session, String requestheader)
26 | {
27 | //判断是否存在过滤项
28 | if (Include == null || Include.Length < 1) return requestheader;
29 |
30 | //取得请求Url
31 | var uri = HttpHelper.GetUriFromHeader(requestheader);
32 | if (uri == null) return requestheader;
33 |
34 | //过滤检查
35 | foreach (var item in Include)
36 | {
37 | if (uri.OriginalString.Contains(item))
38 | {
39 | //检查例外
40 | if (Exclude != null && Exclude.Length > 0)
41 | {
42 | var isexclude = false;
43 | foreach (var elm in Exclude)
44 | {
45 | if (uri.OriginalString.Contains(elm))
46 | {
47 | isexclude = true;
48 | break;
49 | }
50 | }
51 | if (isexclude) continue;
52 | }
53 |
54 | //缓存处理
55 | ProcessCache(session, uri);
56 |
57 | //禁止往下
58 | return null;
59 | }
60 | }
61 |
62 | return requestheader;
63 | }
64 | #endregion
65 |
66 | ///
67 | /// 默认设置
68 | ///
69 | public override PluginConfig DefaultConfig
70 | {
71 | get
72 | {
73 | var pc = base.DefaultConfig;
74 | pc.Name = "Http缓存";
75 | pc.Author = "大石头";
76 | return pc;
77 | }
78 | }
79 | #endregion
80 |
81 | #region 属性
82 | ///
83 | /// 过滤Url数组。使用Extend,分号隔开
84 | ///
85 | private String[] Include
86 | {
87 | get
88 | {
89 | if (Config == null || String.IsNullOrEmpty(Config.Extend)) return null;
90 |
91 | return Config.Extend.Split(new Char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
92 | }
93 | }
94 |
95 | ///
96 | /// 例外Url数组。使用Extend2,分号隔开
97 | ///
98 | private String[] Exclude
99 | {
100 | get
101 | {
102 | if (Config == null || String.IsNullOrEmpty(Config.Extend2)) return null;
103 |
104 | return Config.Extend2.Split(new Char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
105 | }
106 | }
107 | #endregion
108 |
109 | #region 扩展
110 | ///
111 | /// 处理缓存
112 | ///
113 | ///
114 | ///
115 | private void ProcessCache(Session session, Uri uri)
116 | {
117 | var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HttpCache");
118 | if (!Directory.Exists(path)) Directory.CreateDirectory(path);
119 | var filename = uri.AbsolutePath;
120 | filename = Path.Combine(path, filename);
121 | filename = Path.GetFullPath(filename);
122 |
123 | //缓存是否存在
124 | if (!File.Exists(filename))
125 | {
126 | WriteLog("无法找到" + filename + "的缓存,准备下载!");
127 | //没找到,下载
128 | if (!Directory.Exists(Path.GetDirectoryName(filename)))
129 | Directory.CreateDirectory(Path.GetDirectoryName(filename));
130 | var wc = new WebClient();
131 | try
132 | {
133 | wc.DownloadFile(uri, filename);
134 | var header = wc.ResponseHeaders.ToString();
135 | }
136 | catch (Exception ex)
137 | {
138 | WriteLog("下载文件 " + uri.ToString() + " 出错!" + ex.ToString());
139 | }
140 | }
141 |
142 | //读取缓存
143 | Byte[] bts;
144 | using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
145 | {
146 | fs.Position = 0;
147 | bts = new Byte[fs.Length];
148 | fs.Read(bts, 0, bts.Length);
149 | }
150 | Write(session, uri, bts);
151 | }
152 |
153 | private void Write(Session session, Uri uri, Byte[] Data)
154 | {
155 | var sb = new StringBuilder();
156 | sb.AppendLine("HTTP/1.1 200 OK");
157 | sb.AppendFormat("Content-Length: {0}\r\n", Data.Length);
158 | var ctype = ContentType.Other;
159 | var ext = Path.GetExtension(uri.LocalPath).ToLower();
160 | switch (ext)
161 | {
162 | case ".xml":
163 | ctype = ContentType.Xml;
164 | break;
165 | case ".inf":
166 | ctype = ContentType.Inf;
167 | break;
168 | case ".zip":
169 | ctype = ContentType.Zip;
170 | break;
171 | case ".ini":
172 | ctype = ContentType.Ini;
173 | break;
174 | case ".rp":
175 | case ".zip1":
176 | ctype = ContentType.Rp;
177 | break;
178 | }
179 | sb.AppendFormat("Content-Type: {0}\r\n", ctype);
180 | sb.AppendLine("Last-Modified: Tue, 22 Jan 2008 11:26:53 GMT");
181 | sb.AppendLine("Accept-Ranges: bytes");
182 | sb.AppendLine("ETag: W/\"3415b0e95cc81:53b\"");
183 | sb.AppendLine("Server: Microsoft-IIS/6.0");
184 | sb.AppendLine("X-Powered-By: ASP.NET");
185 | sb.AppendLine("Date: Tue, 22 Jan 2008 14:11:14 GMT");
186 | sb.AppendLine("X-Cache: MISS from CNC-TJ-167-83.fastcdn.com");
187 | sb.AppendLine("Age: 983");
188 | sb.AppendLine("X-Cache: HIT from CNC-HBCZ-2-73.fastcdn.com");
189 | sb.AppendLine("Connection: keep-alive");
190 | sb.AppendLine("");
191 | //sb.AppendLine("");
192 | //回发数据
193 | Data = ByteHelper.Cat(Encoding.ASCII.GetBytes(sb.ToString()), Data);
194 | //session.SendToClient(Encoding.ASCII.GetBytes(sb.ToString()));
195 | //session.SendToClient(Data);
196 | }
197 | #endregion
198 | }
199 |
200 | internal static class ContentType
201 | {
202 | public static String Zip = "application/x-zip-compressed";
203 | public static String Xml = "text/xml";
204 | public static String Inf = "application/octet-stream";
205 | public static String Ini = "application/all";
206 | public static String Rp = "application/all";
207 | public static String Dat = "application/octet-stream";
208 | public static String Dll = "application/octet-stream";
209 | public static String Exe = "application/octet-stream";
210 | public static String Other = "application/octet-stream";
211 | }
212 | }
--------------------------------------------------------------------------------
/XProxy/Http/Plugin/Indirect.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Http/Plugin/Indirect.cs
--------------------------------------------------------------------------------
/XProxy/Http/Plugin/Reverse.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Http/Plugin/Reverse.cs
--------------------------------------------------------------------------------
/XProxy/NetData.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/NetData.cs
--------------------------------------------------------------------------------
/XProxy/NetHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Net.Sockets;
5 | using System.Runtime.InteropServices;
6 | using System.Diagnostics;
7 | using NewLife.Log;
8 |
9 | namespace XProxy
10 | {
11 | ///
12 | /// 网络助手
13 | ///
14 | public static class NetHelper
15 | {
16 | ///
17 | /// 设置超时检测时间和检测间隔
18 | ///
19 | /// 要设置的Socket对象
20 | /// 是否启用Keep-Alive
21 | /// 多长时间后开始第一次探测(单位:毫秒)
22 | /// 探测时间间隔(单位:毫秒)
23 | public static void SetKeepAlive(Socket socket,Boolean iskeepalive, Int32 starttime, Int32 interval)
24 | {
25 | UInt32 dummy = 0;
26 | var inOptionValues = new Byte[Marshal.SizeOf(dummy) * 3];
27 | BitConverter.GetBytes((UInt32)1).CopyTo(inOptionValues, 0);
28 | BitConverter.GetBytes((UInt32)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
29 | BitConverter.GetBytes((UInt32)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
30 | socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
31 | }
32 | //struct tcp_keepalive
33 | //{
34 | // u_long onoff; //是否启用Keep-Alive
35 | // u_long keepalivetime; //多长时间后开始第一次探测(单位:毫秒)
36 | // u_long keepaliveinterval; //探测时间间隔(单位:毫秒)
37 | //};
38 |
39 | ///
40 | /// 输出堆栈
41 | ///
42 | public static void OutStack()
43 | {
44 | var st = new StackTrace(1);
45 | var sb = new StringBuilder();
46 | var sfs = st.GetFrames();
47 | foreach (var sf in sfs)
48 | {
49 | sb.Append(sf.GetMethod().DeclaringType.FullName);
50 | sb.Append(".");
51 | sb.Append(sf.GetMethod().Name);
52 | var s = sf.GetFileName();
53 | if (!String.IsNullOrEmpty(s))
54 | {
55 | sb.Append("(");
56 | sb.Append(s);
57 | sb.Append(",");
58 | sb.Append(sf.GetFileLineNumber().ToString());
59 | sb.Append("行)");
60 | }
61 | sb.Append(Environment.NewLine);
62 | }
63 | XTrace.WriteLine(sb.ToString());
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/XProxy/Plugin/EncryptPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using XProxy.Config;
5 | using XProxy.Base;
6 |
7 | namespace XProxy.Plugin
8 | {
9 | ///
10 | /// 加密插件。端口重定向的过程中,对数据进行加解密
11 | ///
12 | public class EncryptPlugin : PluginBase
13 | {
14 | ///
15 | /// 客户端向服务器发数据时触发。
16 | ///
17 | /// 客户端
18 | /// 数据
19 | /// 经过处理后的数据
20 | public override Byte[] OnClientToServer(Session session, Byte[] Data)
21 | {
22 | return Encrypt(Data);
23 | }
24 |
25 | ///
26 | /// 服务器相客户端发数据时触发。
27 | ///
28 | /// 客户端
29 | /// 数据
30 | /// 经过处理后的数据
31 | public override Byte[] OnServerToClient(Session session, Byte[] Data)
32 | {
33 | return Encrypt(Data);
34 | }
35 |
36 | private Byte[] Encrypt(Byte[] Data)
37 | {
38 | if (Data == null || Data.Length < 1) return null;
39 | for (var i = 0; i < Data.Length; i++)
40 | {
41 | Data[i] ^= EncryptKey;
42 | }
43 | return Data;
44 | }
45 |
46 | ///
47 | /// 默认设置
48 | ///
49 | public override PluginConfig DefaultConfig
50 | {
51 | get
52 | {
53 | var pc = base.DefaultConfig;
54 | pc.Author = "大石头";
55 | pc.Name = "加密插件";
56 | return pc;
57 | }
58 | }
59 |
60 | ///
61 | /// 密钥。目前只能是单个字符,
62 | /// 因为数据包在传输的过程中可能会被重新组合,
63 | /// 从而不能使用块加密
64 | ///
65 | private Byte EncryptKey
66 | {
67 | get
68 | {
69 | if (Config == null || String.IsNullOrEmpty(Config.Extend)) return (Byte)'X';
70 | return (Byte)Config.Extend[0];
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/XProxy/Plugin/IPlugin.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Plugin/IPlugin.cs
--------------------------------------------------------------------------------
/XProxy/Plugin/NATPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using XProxy.Config;
5 |
6 | namespace XProxy.Plugin
7 | {
8 | ///
9 | /// NAT插件。端口重定向
10 | ///
11 | public class NATPlugin : PluginBase
12 | {
13 | ///
14 | /// 默认设置
15 | ///
16 | public override PluginConfig DefaultConfig
17 | {
18 | get
19 | {
20 | var pc = base.DefaultConfig;
21 | pc.Author = "大石头";
22 | pc.Name = "NAT插件";
23 | return pc;
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/XProxy/Plugin/PluginBase.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/Plugin/PluginBase.cs
--------------------------------------------------------------------------------
/XProxy/Plugin/PluginManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Reflection;
5 | using XProxy.Base;
6 | using XProxy.Config;
7 | using XProxy.Http;
8 |
9 | namespace XProxy.Plugin
10 | {
11 | ///
12 | /// 插件管理类
13 | ///
14 | public class PluginManager
15 | {
16 | #region 属性
17 | ///
18 | /// 插件集合。处理各事件的时候,将按照先后顺序调用插件的处理方法。
19 | ///
20 | public IList Plugins { get; set; }
21 |
22 | ///
23 | /// 当前监听器
24 | ///
25 | public Listener Listener { get; set; }
26 |
27 | ///
28 | /// 写日志事件
29 | ///
30 | public event WriteLogDelegate OnWriteLog;
31 | #endregion
32 |
33 | #region 加载插件
34 | ///
35 | /// 加载插件
36 | ///
37 | ///
38 | private void LoadPlugin(IList configs)
39 | {
40 | if (configs == null || configs.Count < 1) return;
41 | var list = new List();
42 | foreach (var config in configs)
43 | {
44 | if (!String.IsNullOrEmpty(config.ClassName))
45 | {
46 | Assembly asm;
47 | if (String.IsNullOrEmpty(config.Path))
48 | asm = Assembly.GetExecutingAssembly();
49 | else
50 | {
51 | var path = config.Path;
52 | if (!Path.IsPathRooted(path)) path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
53 | try
54 | {
55 | asm = Assembly.LoadFile(path);
56 | }
57 | catch (Exception ex)
58 | {
59 | WriteLog(String.Format("加载插件出错:{0}\n{1}", config, ex.ToString()));
60 | continue;
61 | }
62 | }
63 | if (asm != null)
64 | {
65 | var t = asm.GetType(config.ClassName, false);
66 | if (t == null)
67 | {
68 | var ts = asm.GetTypes();
69 | foreach (var tt in ts)
70 | {
71 | if (tt.Name.Contains(config.ClassName))
72 | {
73 | t = tt;
74 | break;
75 | }
76 | }
77 | }
78 | if (t != null)
79 | {
80 | var obj = Activator.CreateInstance(t);
81 | if (obj != null)
82 | {
83 | var p = obj as IPlugin;
84 | if (p != null)
85 | {
86 | p.Config = config;
87 | list.Add(p);
88 | WriteLog(String.Format("加载插件:{0}", config));
89 | }
90 | }
91 | }
92 | }
93 | }
94 | }
95 | Plugins = list;
96 | }
97 | #endregion
98 |
99 | #region 事件
100 | ///
101 | /// 初始化
102 | ///
103 | /// 监听器
104 | public void OnInit(Listener listener)
105 | {
106 | LoadPlugin(listener.Config.Plugins);
107 | if (Plugins == null || Plugins.Count < 1) return;
108 |
109 | for (var i = 0; i < Plugins.Count; i++)
110 | {
111 | Plugins[i].OnWriteLog += new WriteLogDelegate(WriteLog);
112 | Plugins[i].OnInit(this);
113 | }
114 | }
115 |
116 | ///
117 | /// 释放资源
118 | ///
119 | public void OnDispose()
120 | {
121 | if (Plugins == null || Plugins.Count < 1) return;
122 |
123 | for (var i = 0; i < Plugins.Count; i++)
124 | {
125 | Plugins[i].Dispose();
126 | }
127 | }
128 |
129 | ///
130 | /// 开始监听
131 | ///
132 | /// 监听器
133 | public void OnListenerStart(Listener listener)
134 | {
135 | if (Plugins == null || Plugins.Count < 1) return;
136 |
137 | for (var i = 0; i < Plugins.Count; i++)
138 | {
139 | Plugins[i].OnListenerStart(listener);
140 | }
141 | }
142 |
143 | ///
144 | /// 停止监听
145 | ///
146 | /// 监听器
147 | public void OnListenerStop(Listener listener)
148 | {
149 | if (Plugins == null || Plugins.Count < 1) return;
150 |
151 | for (var i = 0; i < Plugins.Count; i++)
152 | {
153 | Plugins[i].OnListenerStop(listener);
154 | }
155 | }
156 |
157 | ///
158 | /// 第一次向客户端发数据时触发。
159 | ///
160 | /// 客户端
161 | /// 是否允许通行
162 | public Boolean OnClientStart(Session session)
163 | {
164 | if (Plugins == null || Plugins.Count < 1) return true;
165 |
166 | for (var i = 0; i < Plugins.Count; i++)
167 | {
168 | if (!Plugins[i].OnClientStart(session)) return false;
169 | }
170 | return true;
171 | }
172 |
173 | ///
174 | /// 连接远程服务器时触发。
175 | ///
176 | /// 客户端
177 | /// 是否允许通行
178 | public Boolean OnServerStart(Session session)
179 | {
180 | if (Plugins == null || Plugins.Count < 1) return true;
181 |
182 | for (var i = 0; i < Plugins.Count; i++)
183 | {
184 | if (!Plugins[i].OnServerStart(session)) return false;
185 | }
186 | return true;
187 | }
188 | #endregion
189 |
190 | #region 数据交换事件
191 | ///
192 | /// 客户端向服务器发数据时触发。
193 | ///
194 | /// 客户端
195 | /// 数据
196 | /// 经过处理后Data参数中的数据大小
197 | public Byte[] OnClientToServer(Session session, Byte[] Data)
198 | {
199 | if (Plugins == null || Plugins.Count < 1) return Data;
200 | if (Data == null || Data.Length < 1) return null;
201 |
202 | for (var i = 0; i < Plugins.Count; i++)
203 | {
204 | Data = Plugins[i].OnClientToServer(session, Data);
205 | if (Data == null || Data.Length < 1) return null;
206 | }
207 | return Data;
208 | }
209 |
210 | ///
211 | /// 服务器相客户端发数据时触发。
212 | ///
213 | /// 客户端
214 | /// 数据
215 | /// 经过处理后Data参数中的数据大小
216 | public Byte[] OnServerToClient(Session session, Byte[] Data)
217 | {
218 | if (Plugins == null || Plugins.Count < 1) return Data;
219 | if (Data == null || Data.Length < 1) return null;
220 |
221 | for (var i = 0; i < Plugins.Count; i++)
222 | {
223 | Data = Plugins[i].OnServerToClient(session, Data);
224 | if (Data == null || Data.Length < 1) return null;
225 | }
226 | return Data;
227 | }
228 | #endregion
229 |
230 | #region 日志
231 | ///
232 | /// 写日志
233 | ///
234 | /// 日志
235 | public void WriteLog(String msg)
236 | {
237 | if (OnWriteLog != null)
238 | {
239 | if (Listener == null)
240 | OnWriteLog(msg);
241 | else
242 | OnWriteLog(String.Format("[{0}] {1}", Listener.Config.Name, msg));
243 | }
244 | }
245 | #endregion
246 |
247 | #region 静态 插件管理
248 | private static PluginConfig[] _AllPlugins;
249 | ///
250 | /// 所有插件
251 | ///
252 | public static PluginConfig[] AllPlugins
253 | {
254 | get
255 | {
256 | LoadAllAssembly();
257 |
258 | var list = new List();
259 | foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
260 | {
261 | foreach (var t in asm.GetTypes())
262 | {
263 | if (t.GetInterface(typeof(IPlugin).Name) != null)
264 | {
265 | try
266 | {
267 | var ip = asm.CreateInstance(t.FullName) as IPlugin;
268 | if (ip != null) list.Add(ip.DefaultConfig);
269 | }
270 | catch { }
271 | }
272 | }
273 | }
274 | _AllPlugins = new PluginConfig[list.Count];
275 | list.CopyTo(_AllPlugins);
276 |
277 | return _AllPlugins;
278 | }
279 | }
280 |
281 | private static PluginConfig[] _AllHttpPlugins;
282 | ///
283 | /// 所有Http插件
284 | ///
285 | public static PluginConfig[] AllHttpPlugins
286 | {
287 | get
288 | {
289 | LoadAllAssembly();
290 |
291 | var list = new List();
292 | foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
293 | {
294 | foreach (var t in asm.GetTypes())
295 | {
296 | //加上IsAbstract的判断,防止把抽象插件基类识别为插件
297 | if (!t.IsAbstract && t.GetInterface(typeof(IHttpPlugin).Name) != null)
298 | {
299 | try
300 | {
301 | var ip = asm.CreateInstance(t.FullName) as IHttpPlugin;
302 | if (ip != null) list.Add(ip.DefaultConfig);
303 | }
304 | catch { }
305 | }
306 | }
307 | }
308 | _AllHttpPlugins = new PluginConfig[list.Count];
309 | list.CopyTo(_AllHttpPlugins);
310 |
311 | return _AllHttpPlugins;
312 | }
313 | }
314 |
315 | private static void LoadAllAssembly()
316 | {
317 | var path = AppDomain.CurrentDomain.BaseDirectory;
318 | //path = Path.Combine(path, "Plugins");
319 | if (!Directory.Exists(path)) return;
320 |
321 | //不能使用AllDirectories,当很多子目录的时候,会卡死程序
322 | var fs = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly);
323 | if (fs == null || fs.Length < 1) return;
324 |
325 | foreach (var s in fs)
326 | {
327 | try
328 | {
329 | Assembly.LoadFile(s);
330 | }
331 | catch { }
332 | }
333 | }
334 | #endregion
335 | }
336 | }
--------------------------------------------------------------------------------
/XProxy/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using NewLife;
7 | using NewLife.Agent;
8 | using NewLife.Log;
9 | using NewLife.Net;
10 | using NewLife.Net.Proxy;
11 | using NewLife.Reflection;
12 | using NewLife.Serialization;
13 | using NewLife.Threading;
14 | using Stardust;
15 | using XProxy.Proxy;
16 |
17 | namespace XProxy
18 | {
19 | class Program
20 | {
21 | static void Main(String[] args) => new MyService().Main(args);
22 | }
23 |
24 | class MyService : ServiceBase
25 | {
26 | public MyService()
27 | {
28 | ServiceName = "XProxy";
29 | DisplayName = "XProxy代理服务器";
30 |
31 | Task.Run(() => ShowAll());
32 | }
33 |
34 | private TimerX _timer;
35 | private readonly IDictionary _ps = new Dictionary();
36 | private void CheckProxy(Object state)
37 | {
38 | var set = Setting.Current;
39 | foreach (var item in set.Items)
40 | {
41 | if (item.Enable)
42 | {
43 | if (!_ps.ContainsKey(item.Name) && !item.Provider.IsNullOrEmpty())
44 | {
45 | try
46 | {
47 | // 创建代理实例
48 | var proxy = CreateProxy(item, set.Debug);
49 | if (proxy != null) _ps.Add(item.Name, proxy);
50 | }
51 | catch (Exception ex)
52 | {
53 | XTrace.WriteException(ex);
54 |
55 | item.Enable = false;
56 | }
57 | }
58 | }
59 | else
60 | {
61 | // 停止配置为停用的服务
62 | if (_ps.TryGetValue(item.Name, out var pi))
63 | {
64 | _ps.Remove(item.Name);
65 |
66 | using var span = _tracer?.NewSpan("StopProxy", pi.Name);
67 |
68 | pi.Stop("配置停用");
69 | }
70 | }
71 | }
72 | }
73 |
74 | private ITracer _tracer;
75 | public override void StartWork(String reason)
76 | {
77 | var star = new StarFactory(null, null, null);
78 | if (!star.Server.IsNullOrEmpty()) _tracer = star.Tracer;
79 |
80 | //CheckProxy();
81 | // 检查运行时新增代理配置
82 | _timer = new TimerX(CheckProxy, null, 100, 10_000) { Async = true };
83 |
84 | base.StartWork(reason);
85 | }
86 |
87 | public override void StopWork(String reason)
88 | {
89 | _timer.TryDispose();
90 | _timer = null;
91 |
92 | foreach (var item in _ps)
93 | {
94 | item.Value.Stop(reason);
95 | }
96 | _ps.TryDispose();
97 |
98 | base.StopWork(reason);
99 | }
100 |
101 | #region 辅助
102 | private void ShowAll()
103 | {
104 | //await Task.Delay(500);
105 | Thread.Sleep(500);
106 |
107 | //XTrace.WriteLine("");
108 | Console.WriteLine();
109 |
110 | var ps = ProxyHelper.GetAll();
111 | XTrace.WriteLine("共有代理提供者[{0}]:", ps.Length);
112 | foreach (var item in ps)
113 | {
114 | var pxy = item.CreateInstance() as ProxyBase;
115 | XTrace.WriteLine("{0}\t{1}\t{2}", pxy.Name, item.GetDisplayName(), item.FullName);
116 | }
117 |
118 | Console.WriteLine();
119 | var set = Setting.Current;
120 | var xs = set.Items ?? new ProxyItem[0];
121 | XTrace.WriteLine("共有代理配置[{0}]:", xs.Length);
122 | foreach (var item in xs)
123 | {
124 | XTrace.WriteLine("{0}\t{1}\t{2}\t{3}=>{4}\t{5}", item.Name, item.Provider, item.Enable, item.Local, item.Remote, item.Config);
125 | }
126 | }
127 |
128 | private ProxyBase CreateProxy(ProxyItem item, Boolean debug)
129 | {
130 | var xs = ProxyHelper.GetAll();
131 | var type = xs.FirstOrDefault(e => item.Provider.EqualIgnoreCase(e.Name, e.FullName, e.Name.TrimEnd("Proxy")));
132 | if (type == null) return null;
133 |
134 | if (type.CreateInstance() is not ProxyBase proxy) return null;
135 |
136 | using var span = _tracer?.NewSpan("CreateProxy", item);
137 |
138 | proxy.Name = item.Name;
139 | proxy.Tracer = _tracer;
140 |
141 | XTrace.WriteLine("创建代理 {0}", item.ToJson());
142 |
143 | proxy.Init(item.Config);
144 |
145 | // 配置本地、远程参数。高级参数直接修改这里,解析item.Value
146 | proxy.Local = new NetUri(item.Local);
147 | if (proxy is NATProxy nat && !item.Remote.IsNullOrEmpty()) nat.RemoteServer = new NetUri(item.Remote);
148 |
149 | // 配置日志
150 | proxy.Log = XTrace.Log;
151 | if (debug)
152 | {
153 | proxy.SessionLog = XTrace.Log;
154 | proxy.Debug = debug;
155 | }
156 |
157 | if (XTrace.Log.Level <= LogLevel.Debug)
158 | {
159 | proxy.SocketLog = XTrace.Log;
160 | proxy.LogSend = true;
161 | proxy.LogReceive = true;
162 | }
163 |
164 | // 启动服务
165 | proxy.Start();
166 |
167 | return proxy;
168 | }
169 | #endregion
170 | }
171 | }
--------------------------------------------------------------------------------
/XProxy/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过下列属性集
6 | // 控制。更改这些属性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("XProxy代理服务器")]
9 | [assembly: AssemblyDescription("反向代理")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("新生命开发团队")]
12 | [assembly: AssemblyProduct("XProxy")]
13 | [assembly: AssemblyCopyright("版权所有 (C) 新生命开发团队 2008")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 属性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("14b28417-415c-47c3-bc6e-c68c20a3f4ac")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 内部版本号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("2.0.*")]
36 | [assembly: AssemblyFileVersion("2.0.2018.0924")]
37 |
38 | /*
39 | * v2.0.2018.0924 使用第三代网络库重构反向代理
40 | * v1.1.2008.0307 完善配置管理界面
41 | * v1.0.2005.0101 建立代理服务器
42 | */
43 |
--------------------------------------------------------------------------------
/XProxy/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | Release
9 | Any CPU
10 | net5
11 | ..\BinServer\publish\
12 | false
13 | <_IsPortable>true
14 | linux-arm
15 | False
16 |
17 |
--------------------------------------------------------------------------------
/XProxy/Proxy/HttpIpProxy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.NetworkInformation;
6 | using System.Threading;
7 | using NewLife.Http;
8 | using NewLife.Net.Http;
9 | using NewLife.Security;
10 | using NewLife.Threading;
11 |
12 | namespace NewLife.Net.Proxy
13 | {
14 | /// 代理服务器使用本地IP的模式
15 | public enum IpModes
16 | {
17 | /// 任意IP
18 | Any,
19 |
20 | /// 轮询
21 | Round,
22 |
23 | /// 随机
24 | Random,
25 |
26 | /// 同源,从进来的IP出去
27 | Origin,
28 | }
29 |
30 | /// 支持使用多个本地IP地址的Http代理服务器
31 | public class HttpIpProxy : HttpProxy
32 | {
33 | #region 属性
34 | /// 本地地址集合
35 | public IPAddress[] Addresses { get; set; }
36 |
37 | /// 使用IP的模式。支持any/round/random/origin
38 | public IpModes IpMode { get; set; }
39 |
40 | private Int32 _index;
41 | private TimerX _timer;
42 | private String _lastAddresses;
43 | #endregion
44 |
45 | public override void Init(String config)
46 | {
47 | var dic = config?.SplitAsDictionary("=", ";");
48 | if (dic != null && dic.Count > 0)
49 | {
50 | if (Enum.TryParse(dic["IpMode"], true, out var mode)) IpMode = mode;
51 | }
52 |
53 | base.Init(config);
54 | }
55 |
56 | protected override void OnStart()
57 | {
58 | WriteLog("IpMode: {0}", IpMode);
59 |
60 | _timer = new TimerX(DoGetIps, null, 0, 15_000) { Async = true };
61 |
62 | base.OnStart();
63 | }
64 |
65 | protected override void OnStop(String reason)
66 | {
67 | _timer.TryDispose();
68 |
69 | base.OnStop(reason);
70 | }
71 |
72 | private void DoGetIps(Object state)
73 | {
74 | //var addrs = NetHelper.GetIPs().ToArray();
75 | var addrs = new List();
76 | foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
77 | {
78 | if (item.OperationalStatus != OperationalStatus.Up) continue;
79 | if (item.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
80 |
81 | var ipp = item.GetIPProperties();
82 | if (ipp != null && ipp.UnicastAddresses.Count > 0)
83 | {
84 | var gw = ipp.GatewayAddresses.Count;
85 | if (gw == 0) continue;
86 |
87 | foreach (var elm in ipp.UnicastAddresses)
88 | {
89 | try
90 | {
91 | if (elm.DuplicateAddressDetectionState != DuplicateAddressDetectionState.Preferred) continue;
92 | }
93 | catch { }
94 |
95 | if (elm.Address.IsIPv4()) addrs.Add(elm.Address);
96 | }
97 | }
98 | }
99 |
100 | var str = addrs.Join();
101 |
102 | if (!_lastAddresses.IsNullOrEmpty() && _lastAddresses == str) return;
103 | _lastAddresses = str;
104 |
105 | WriteLog("可用本机地址:{0}", str);
106 |
107 | Addresses = addrs.ToArray();
108 | }
109 |
110 | /// 创建会话
111 | ///
112 | ///
113 | protected override INetSession CreateSession(ISocketSession session) => new IpSession();
114 |
115 | private class IpSession : Session
116 | {
117 | #region 方法
118 | protected override ISocketClient CreateRemote(ReceivedEventArgs e)
119 | {
120 | var client = base.CreateRemote(e);
121 |
122 | // 绑定本地IP地址
123 | var proxy = Host as HttpIpProxy;
124 | var addrs = proxy.Addresses;
125 | if (addrs != null && addrs.Length > 0)
126 | {
127 | IPAddress addr = null;
128 |
129 | switch (proxy.IpMode)
130 | {
131 | case IpModes.Any:
132 | break;
133 | case IpModes.Round:
134 | {
135 | var n = Interlocked.Increment(ref proxy._index) - 1;
136 | addr = addrs[n % addrs.Length];
137 | }
138 | break;
139 | case IpModes.Random:
140 | {
141 | var n = Rand.Next(addrs.Length);
142 | addr = addrs[n % addrs.Length];
143 | }
144 | break;
145 | case IpModes.Origin:
146 | {
147 | addr = Session.Local.Address;
148 | if (addr.IsAny() || addr == IPAddress.Loopback || addr == IPAddress.IPv6Loopback) addr = null;
149 |
150 | if (addr != null && !addrs.Any(e => e.Equals(addr)))
151 | {
152 | WriteLog("CreateRemote IpMode={0}, {1} is not in local-ips", proxy.IpMode, addr);
153 | addr = null;
154 | }
155 | }
156 | break;
157 | default:
158 | break;
159 | }
160 |
161 | if (addr != null)
162 | {
163 | client.Local.Address = addr;
164 | WriteLog("CreateRemote IpMode={0}, LocalIp={1}", proxy.IpMode, addr);
165 | }
166 | }
167 |
168 | return client;
169 | }
170 |
171 | /// 收到客户端发来的数据。子类可通过重载该方法来修改数据
172 | ///
173 | /// 修改后的数据
174 | protected override void OnReceiveRemote(ReceivedEventArgs e)
175 | {
176 | var response = new HttpResponse();
177 | if (response.Parse(e.Packet))
178 | {
179 | // 响应头增加所使用的本地IP地址,让客户端知道
180 | response.Headers["Proxy-Local-Ip"] = RemoteServer.Local.Address + "";
181 | e.Packet = response.Build();
182 | }
183 |
184 | base.OnReceiveRemote(e);
185 | }
186 | #endregion
187 | }
188 | }
189 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/HttpProxy.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using NewLife.Http;
3 | using NewLife.Net.Http;
4 |
5 | namespace NewLife.Net.Proxy;
6 |
7 | /// Http代理。可用于代理各种Http通讯请求。
8 | /// Http代理请求与普通请求唯一的不同就是Uri,Http代理请求收到的是可能包括主机名的完整Uri
9 | public class HttpProxy : ProxyBase
10 | {
11 | #region 属性
12 | /// 代理用户名
13 | public String Username { get; set; }
14 |
15 | /// 代理密码
16 | public String Password { get; set; }
17 |
18 | /// 对于被保护区域(即安全域)的描述。如果没有指定安全域,客户端通常用一个格式化的主机名来代替。
19 | public String Realm { get; set; }
20 | #endregion
21 |
22 | #region 构造
23 | /// 实例化
24 | public HttpProxy()
25 | {
26 | Port = 8080;
27 | ProtocolType = NetType.Tcp;
28 | }
29 | #endregion
30 |
31 | #region 方法
32 | public override void Init(String config)
33 | {
34 | var dic = config?.SplitAsDictionary("=", ";");
35 | if (dic != null && dic.Count > 0)
36 | {
37 | Username = dic["user"];
38 | Password = dic["pass"];
39 | Realm = dic["realm"];
40 | }
41 | }
42 | #endregion
43 |
44 | #region 会话
45 | /// 创建会话
46 | ///
47 | ///
48 | protected override INetSession CreateSession(ISocketSession session) => new Session();
49 |
50 | /// Http反向代理会话
51 | public class Session : ProxySession
52 | {
53 | /// 已完成处理,正在转发数据的请求头
54 | public HttpHeader Request { get; set; }
55 |
56 | /// 已认证
57 | public Boolean Authenticated { get; set; }
58 |
59 | /// 收到客户端发来的数据。子类可通过重载该方法来修改数据
60 | ///
61 | /// 如果数据包包括头部和主体,可以分开处理。
62 | /// 最麻烦的就是数据包不是一个完整的头部,还落了一部分在后面的包上。
63 | ///
64 | ///
65 | protected override void OnReceive(ReceivedEventArgs e)
66 | {
67 | if (e.Packet.Total == 0 || !HttpBase.FastValidHeader(e.Packet.GetSpan()))
68 | {
69 | base.OnReceive(e);
70 | return;
71 | }
72 |
73 | var request = new HttpRequest();
74 | if (request.Parse(e.Packet))
75 | {
76 | WriteLog("[{0}]{1} {2}", Remote.Address, request.Method, request.RequestUri);
77 |
78 | using var span = Host.Tracer?.NewSpan("proxy:HttpProxyRequest", request.RequestUri + "");
79 |
80 | if (!OnRequest(request, e)) return;
81 |
82 | e.Packet = request.Build();
83 | }
84 |
85 | base.OnReceive(e);
86 | }
87 |
88 | /// 收到请求时
89 | ///
90 | ///
91 | ///
92 | protected virtual Boolean OnRequest(HttpRequest request, ReceivedEventArgs e)
93 | {
94 | var proxy = (this as INetSession).Host as HttpProxy;
95 |
96 | // 代理认证
97 | if (!proxy.Username.IsNullOrEmpty() && !OnAuthenticate(request)) return false;
98 |
99 | // https使用CONNECT建立连接
100 | if (request.Method.EqualIgnoreCase("CONNECT")) return ProcessConnect(request, e);
101 |
102 | var remote = RemoteServer;
103 | var ruri = RemoteServerUri;
104 | if (request.RequestUri.IsAbsoluteUri)
105 | {
106 | var uri = request.RequestUri;
107 | var host = uri.Host + ":" + uri.Port;
108 |
109 | // 可能不含Host
110 | if (request.Host.IsNullOrEmpty()) request.Host = host;
111 |
112 | // 如果地址或端口改变,则重新连接服务器
113 | if (remote != null && (uri.Host != ruri.Host || uri.Port != ruri.Port))
114 | {
115 | remote.Dispose();
116 | RemoteServer = null;
117 | }
118 |
119 | ruri.Host = uri.Host;
120 | ruri.Port = uri.Port;
121 | request.RequestUri = new Uri(uri.PathAndQuery, UriKind.Relative);
122 | }
123 | else if (!request.Host.IsNullOrEmpty())
124 | {
125 | ruri.Host = request.Host;
126 | ruri.Port = 80;
127 | }
128 | else
129 | throw new XException("无法处理的请求!{0}", request);
130 |
131 | // 处理KeepAlive
132 | if (request.Headers.TryGetValue("Proxy-Connection", out var str))
133 | {
134 | request.KeepAlive = str.EqualIgnoreCase("keep-alive");
135 | request.Headers.Remove("Proxy-Connection");
136 | }
137 |
138 | // 移除所有代理专用头部字段,避免传递给网站服务器
139 | var keys = request.Headers.Where(e => e.Key.StartsWithIgnoreCase("proxy-")).ToArray();
140 | foreach (var item in keys)
141 | {
142 | request.Headers.Remove(item);
143 | }
144 |
145 | return true;
146 | }
147 |
148 | /// 代理认证
149 | ///
150 | ///
151 | protected virtual Boolean OnAuthenticate(HttpRequest request)
152 | {
153 | if (Authenticated) return true;
154 |
155 | var proxy = (this as INetSession).Host as HttpProxy;
156 |
157 | var token = request.Headers["Proxy-Authorization"];
158 | if (!token.IsNullOrEmpty() && token.StartsWithIgnoreCase("Basic "))
159 | {
160 | token = token.Substring("Basic ".Length);
161 | token = token.ToBase64().ToStr();
162 |
163 | var p = token.IndexOf(':');
164 | if (p > 0 && p < token.Length - 1)
165 | {
166 | var user = token.Substring(0, p);
167 | var pass = token.Substring(p + 1);
168 | if (user == proxy.Username && pass == proxy.Password)
169 | {
170 | Authenticated = true;
171 | }
172 | }
173 | }
174 |
175 | if (!Authenticated)
176 | {
177 | var rs = new HttpResponse
178 | {
179 | Version = request.Version,
180 | StatusCode = HttpStatusCode.ProxyAuthenticationRequired,
181 | };
182 |
183 | rs.Headers["Proxy-Authenticate"] = $"Basic realm=\"{proxy.Realm}\"";
184 |
185 | Send(rs.Build());
186 |
187 | return false;
188 | }
189 |
190 | return true;
191 | }
192 |
193 | private Boolean ProcessConnect(HttpRequest request, ReceivedEventArgs e)
194 | {
195 | using var span = Host.Tracer?.NewSpan("proxy:HttpProxyConnect", request.RequestUri + "");
196 |
197 | var pxy = Host as HttpProxy;
198 |
199 | var uri = new NetUri(request.RequestUri.ToString());
200 | var ruri = RemoteServerUri;
201 | ruri.Host = uri.Host;
202 | ruri.Port = uri.Port;
203 |
204 | // 不要连自己,避免死循环
205 | if (uri.Port == pxy.Server.Port && uri.Address.IsLocal())
206 | {
207 | WriteLog("不要连自己,避免死循环");
208 | Dispose();
209 | return false;
210 | }
211 |
212 | var rs = new HttpResponse { Version = request.Version };
213 | try
214 | {
215 | // 连接远程服务器,启动数据交换
216 | if (RemoteServer == null) ConnectRemote(e);
217 |
218 | // 响应头增加所使用的本地IP地址,让客户端知道
219 | rs.Headers["Proxy-Local-Ip"] = RemoteServer?.Local.Address + "";
220 |
221 | rs.StatusCode = HttpStatusCode.OK;
222 | rs.StatusDescription = "OK";
223 | }
224 | #if NET5_0
225 | catch (HttpRequestException ex)
226 | {
227 | rs.StatusCode = ex.StatusCode ?? HttpStatusCode.BadRequest;
228 | rs.StatusDescription = ex.Message;
229 | }
230 | #endif
231 | catch (Exception ex)
232 | {
233 | rs.StatusCode = HttpStatusCode.BadGateway;
234 | rs.StatusDescription = ex.Message;
235 | }
236 |
237 | // 告诉客户端,已经连上了服务端,或者没有连上,这里不需要向服务端发送任何数据
238 | Send(rs.Build());
239 |
240 | return false;
241 | }
242 |
243 | /// 远程连接断开时触发。默认销毁整个会话,子类可根据业务情况决定客户端与代理的链接是否重用。
244 | ///
245 | protected override void OnRemoteDispose(ISocketClient session)
246 | {
247 | // 如果客户端不要求保持连接,就销毁吧
248 | if (Request != null && !Request.KeepAlive) base.OnRemoteDispose(session);
249 | }
250 | }
251 | #endregion
252 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/HttpReverseProxy.cs:
--------------------------------------------------------------------------------
1 | using NewLife.Data;
2 | using NewLife.Net.Http;
3 | using XProxy.Proxy;
4 |
5 | namespace NewLife.Net.Proxy;
6 |
7 | /// Http反向代理。把所有收到的Http请求转发到目标服务器。
8 | ///
9 | /// 主要是修改Http请求头为正确的主机,还有可能修改Http响应。
10 | ///
11 | /// 经典用途:
12 | /// 1,缓存。代理缓存某些静态资源的请求结果,减少对服务器的请求压力
13 | /// 2,拦截。禁止访问某些资源,返回空白页或者连接重置
14 | /// 3,修改请求或响应。更多的可能是修改响应的页面内容
15 | /// 4,记录统计。记录并统计请求的网址。
16 | ///
17 | /// 修改Http响应的一般做法:
18 | /// 1,反向映射888端口到目标abc.com
19 | /// 2,abc.com页面响应时,所有http://abc.com/的连接都修改为http://IP:888
20 | /// 3,注意在内网的反向代理需要使用公网IP,而不是本机IP
21 | /// 4,子域名也可以修改,比如http://pic.abc.com/修改为http://IP:888/http_pic.abc.com/
22 | ///
23 | public class HttpReverseProxy : NATProxy
24 | {
25 | /// 请求时触发。
26 | public event EventHandler OnRequest;
27 |
28 | /// 实例化
29 | public HttpReverseProxy()
30 | {
31 | Name = "HttpReverse";
32 |
33 | Port = 80;
34 | if (RemoteServer.Port == 0) RemoteServer.Port = 80;
35 |
36 | ProtocolType = NetType.Tcp;
37 | }
38 |
39 | /// 创建会话
40 | ///
41 | ///
42 | protected override INetSession CreateSession(ISocketSession session) => new Session();
43 |
44 | #region 会话
45 | /// Http反向代理会话
46 | class Session : ProxySession
47 | {
48 | /// 请求头部
49 | public HttpHeader Request { get; set; }
50 |
51 | /// 属性说明
52 | public String RemoteHost { get; set; }
53 |
54 | /// 原始主机
55 | public String RawHost { get; set; }
56 |
57 | /// 收到客户端发来的数据。子类可通过重载该方法来修改数据
58 | ///
59 | protected override void OnReceive(ReceivedEventArgs e)
60 | {
61 | // 解析请求头
62 | var stream = e.Packet.GetStream();
63 | var entity = HttpHeader.Read(stream, HttpHeaderReadMode.Request);
64 | if (entity == null)
65 | {
66 | base.OnReceive(e);
67 | return;
68 | }
69 |
70 | WriteLog("{3}请求:{0} {1} [{2}]", entity.Method, entity.Url, entity.ContentLength, ID);
71 |
72 | Request = entity;
73 |
74 | var proxy = Host as HttpReverseProxy;
75 | proxy.OnRequest?.Invoke(this, e);
76 |
77 | var host = entity.Url.IsAbsoluteUri ? entity.Url.Host : proxy.RemoteServer.Host;
78 | RemoteHost = host;
79 | RawHost = entity.Host;
80 | entity.Host = host;
81 |
82 | // 引用
83 | var r = entity.Referer;
84 | if (!String.IsNullOrEmpty(r))
85 | {
86 | var ri = new Uri(r, UriKind.RelativeOrAbsolute);
87 | if (ri.IsAbsoluteUri && ri.Authority == RawHost)
88 | {
89 | r = r.Replace(RawHost, host);
90 | entity.Referer = r;
91 | }
92 | }
93 |
94 | //// 取消压缩
95 | //var key = "Accept-Encoding";
96 | //if (entity.Headers.ContainsKey(key)) entity.Headers.Remove(key);
97 |
98 | // 重新构造请求
99 | var ms = new MemoryStream();
100 | entity.Write(ms);
101 | stream.CopyTo(ms);
102 | ms.Position = 0;
103 |
104 | e.Packet = (ArrayPacket)ms.ToArray();
105 |
106 | base.OnReceive(e);
107 | }
108 | }
109 | #endregion
110 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/NATProxy.cs:
--------------------------------------------------------------------------------
1 | using NewLife;
2 | using NewLife.Net;
3 | using NewLife.Net.Proxy;
4 |
5 | namespace XProxy.Proxy;
6 |
7 | /// 通用NAT代理。所有收到的数据,都转发到指定目标
8 | public class NATProxy : ProxyBase
9 | {
10 | #region 属性
11 | /// 远程服务器地址
12 | public NetUri RemoteServer { get; set; } = new NetUri();
13 |
14 | /// 端口数。支持映射一批端口到目标服务器的对应端口上
15 | public Int32 Ports { get; set; }
16 |
17 | /// 前导语。验证第一个包必须包含该前导语
18 | public String Prefix { get; set; }
19 | #endregion
20 |
21 | #region 构造
22 | /// 实例化
23 | public NATProxy() { }
24 |
25 | /// 实例化
26 | /// 目标服务器地址
27 | /// 目标服务器端口
28 | public NATProxy(String hostname, Int32 port) : this(hostname, port, NetType.Tcp) { }
29 |
30 | /// 实例化
31 | /// 目标服务器地址
32 | /// 目标服务器端口
33 | /// 协议
34 | public NATProxy(String hostname, Int32 port, NetType protocol)
35 | : this() => RemoteServer = new NetUri(protocol, hostname, port);
36 | #endregion
37 |
38 | #region 方法
39 | public override void Init(String config)
40 | {
41 | var dic = config?.SplitAsDictionary("=", ";");
42 | if (dic != null && dic.Count > 0)
43 | {
44 | Ports = dic["Ports"].ToInt();
45 | Prefix = dic["Prefix"];
46 | }
47 | }
48 |
49 | /// 开始
50 | protected override void OnStart()
51 | {
52 | WriteLog("NAT代理 => {0}", RemoteServer);
53 |
54 | if (RemoteServer.Type == 0) RemoteServer.Type = ProtocolType;
55 |
56 | // 多端口
57 | if (Ports > 1)
58 | for (var i = 0; i < Ports; i++)
59 | {
60 | var list = CreateServer(Local.Address, Port + i, Local.Type, AddressFamily);
61 | foreach (var item in list)
62 | AttachServer(item);
63 | }
64 |
65 | base.OnStart();
66 | }
67 |
68 | /// 创建会话
69 | ///
70 | ///
71 | protected override INetSession CreateSession(ISocketSession session) => new NATSession { Host = this };
72 |
73 | /// 添加会话。子类可以在添加会话前对会话进行一些处理
74 | ///
75 | protected override void AddSession(INetSession session)
76 | {
77 | var svr = RemoteServer;
78 |
79 | // 多端口
80 | if (Ports > 1)
81 | {
82 | // 算出来当前端口的索引位置
83 | var port = session.Session.Local.Port;
84 | var index = port - Port;
85 |
86 | // 计算目标端口
87 | svr = new NetUri(svr.ToString());
88 | svr.Port += index;
89 | }
90 |
91 | var ps = session as ProxySession;
92 | ps.RemoteServerUri = svr;
93 | // 如果不是Tcp/Udp,则使用本地协议
94 | if (!RemoteServer.IsTcp && !RemoteServer.IsUdp)
95 | ps.RemoteServerUri.Type = Local.Type;
96 |
97 | base.AddSession(session);
98 | }
99 | #endregion
100 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/NATSession.cs:
--------------------------------------------------------------------------------
1 | using NewLife;
2 | using NewLife.Data;
3 | using NewLife.Net;
4 | using NewLife.Net.Proxy;
5 |
6 | namespace XProxy.Proxy;
7 |
8 | public class NATSession : ProxySession
9 | {
10 | Int32 _prefix = 0;
11 | protected override void OnReceive(ReceivedEventArgs e)
12 | {
13 | var pk = e.Packet;
14 | if (_prefix == 0 && pk.Total > 0)
15 | {
16 | var pf = (Host as NATProxy).Prefix;
17 | if (!pf.IsNullOrEmpty())
18 | {
19 | if (pk.Slice(0, pf.Length / 2).ToHex() == pf)
20 | _prefix = 1;
21 | else
22 | _prefix = 2;
23 | }
24 | else
25 | _prefix = 1;
26 | }
27 |
28 | if (_prefix == 2) return;
29 |
30 | base.OnReceive(e);
31 | }
32 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/ProxyBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NewLife.Net.Proxy;
4 |
5 | /// 网络数据转发代理基类
6 | ///
7 | /// 网络代理分为本地服务器、客户端、远程服务器三种角色,本地服务器负责监听并转发客户端和远程服务器之间的所有数据。
8 | ///
9 | public abstract class ProxyBase : NetServer
10 | {
11 | #region 属性
12 | /// 开始会话时连接远程会话。默认false
13 | public Boolean ConnectRemoteOnStart { get; set; }
14 |
15 | /// 调试开关
16 | public Boolean Debug { get; set; }
17 | #endregion
18 |
19 | #region 构造函数
20 | ///
21 | public ProxyBase()
22 | {
23 | // 必须要使UseSession = true,否则创建的session对象无Host属性,在ShowSession时,无法获取Host.Name
24 | UseSession = true;
25 | }
26 | #endregion
27 |
28 | #region 业务
29 | /// 初始化配置
30 | ///
31 | public virtual void Init(String config) { }
32 |
33 | /// 创建会话
34 | ///
35 | ///
36 | protected override INetSession CreateSession(ISocketSession session) => new ProxySession { Host = this };
37 |
38 | /// 添加会话
39 | ///
40 | protected override void AddSession(INetSession session)
41 | {
42 | if (session is ProxySession ss) ss.Host = this;
43 |
44 | base.AddSession(session);
45 | }
46 | #endregion
47 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/ProxySession.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using NewLife.Data;
3 | using NewLife.Log;
4 |
5 | namespace NewLife.Net.Proxy;
6 |
7 | /// 代理会话。客户端的一次转发请求(或者Tcp连接),就是一个会话。转发的全部操作都在会话中完成。
8 | ///
9 | /// 一个会话应该包含两端,两个Socket,服务端和客户端
10 | /// 客户端发来的数据,在这里经过一系列过滤器后,转发给服务端;
11 | /// 服务端返回的数据,在这里经过过滤器后,转发给客户端。
12 | ///
13 | public class ProxySession : NetSession
14 | {
15 | #region 属性
16 | /// 主机
17 | public ProxyBase Host { get; set; }
18 |
19 | /// 远程服务端。跟目标服务端通讯的那个Socket,其实是客户端TcpSession/UdpServer
20 | public ISocketClient RemoteServer { get; set; }
21 |
22 | /// 服务端地址
23 | public NetUri RemoteServerUri { get; set; } = new NetUri();
24 |
25 | /// 是否中转空数据包。默认true
26 | public Boolean ExchangeEmptyData { get; set; } = true;
27 |
28 | FileStream _logClient;
29 | FileStream _logServer;
30 | #endregion
31 |
32 | #region 构造
33 | /// 实例化一个代理会话
34 | public ProxySession() { }
35 |
36 | /// 子类重载实现资源释放逻辑时必须首先调用基类方法
37 | /// 从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)
38 | protected override void Dispose(Boolean disposing)
39 | {
40 | base.Dispose(disposing);
41 |
42 | var remote = RemoteServer;
43 | if (remote != null)
44 | {
45 | RemoteServer = null;
46 | remote.TryDispose();
47 | }
48 |
49 | _logClient.TryDispose();
50 | _logServer.TryDispose();
51 | }
52 | #endregion
53 |
54 | #region 数据交换
55 | void WriteLogFile(String kind, IPacket pk)
56 | {
57 | if (!Host.Debug) return;
58 | if (pk == null || pk.Total == 0) return;
59 |
60 | if (kind == "client")
61 | {
62 | if (_logClient == null)
63 | {
64 | var time = DateTime.Now;
65 | var path = XTrace.LogPath;
66 | var fileClient = path.CombinePath($"{time:yyyyMMddHHmmss}_{ID}_client.bin");
67 | _logClient = fileClient.AsFile().OpenWrite();
68 | }
69 | pk.CopyTo(_logClient);
70 | }
71 | else if (kind == "server")
72 | {
73 | if (_logServer == null)
74 | {
75 | var time = DateTime.Now;
76 | var path = XTrace.LogPath;
77 | var fileClient = path.CombinePath($"{time:yyyyMMddHHmmss}_{ID}_server.bin");
78 | _logServer = fileClient.AsFile().OpenWrite();
79 | }
80 | pk.CopyTo(_logServer);
81 | }
82 | }
83 |
84 | /// 开始会话处理。
85 | public override void Start()
86 | {
87 | // 如果未指定远程协议,则与来源协议一致
88 | if (RemoteServerUri.Type == 0) RemoteServerUri.Type = Session.Local.Type;
89 |
90 | //// 如果是Tcp,收到空数据时不要断开。为了稳定可靠,默认设置
91 | //if (Session is TcpSession tcp) tcp.DisconnectWhenEmptyData = false;
92 |
93 | if (Host is ProxyBase proxy && proxy.ConnectRemoteOnStart) ConnectRemote(null);
94 |
95 | base.Start();
96 | }
97 |
98 | /// 收到客户端发来的数据
99 | ///
100 | protected override void OnReceive(ReceivedEventArgs e)
101 | {
102 | if (Disposed) return;
103 |
104 | var len = e.Packet.Total;
105 | if (len > 0 || len == 0 && ExchangeEmptyData)
106 | {
107 | using var span = Host.Tracer?.NewSpan("proxy:OnReceive", Remote + "");
108 | if (len > 0) WriteLogFile("client", e.Packet);
109 |
110 | // 如果未建立到远程服务器链接,则建立
111 | if (RemoteServer == null) ConnectRemote(e);
112 |
113 | // 如果已存在到远程服务器的链接,则把数据发向远程服务器
114 | if (RemoteServer != null) SendToRemote(e.Packet);
115 | }
116 | }
117 |
118 | /// 开始远程连接
119 | ///
120 | protected virtual void ConnectRemote(ReceivedEventArgs e)
121 | {
122 | if (RemoteServer != null) return;
123 | lock (this)
124 | {
125 | if (RemoteServer != null) return;
126 |
127 | using var span = Host.Tracer?.NewSpan("proxy:ConnectRemote", RemoteServerUri + "");
128 |
129 | var start = DateTime.Now;
130 | ISocketClient session = null;
131 | try
132 | {
133 | //WriteDebugLog("连接远程服务器 {0} 解析 {1}", RemoteServerUri, RemoteServerUri.Address);
134 |
135 | session = CreateRemote(e);
136 | //session.Log = Log;
137 | // Socket日志一致
138 | session.Log = Session.Log;
139 | session.OnDisposed += (s, e2) =>
140 | {
141 | // 这个必须清空,是否需要保持会话由OnRemoteDispose决定
142 | RemoteServer = null;
143 | OnRemoteDispose(s as ISocketClient);
144 | };
145 | session.Received += Remote_Received;
146 | session.Open();
147 |
148 | //WriteDebugLog("连接远程服务器成功");
149 |
150 | RemoteServer = session;
151 | }
152 | catch (Exception ex)
153 | {
154 | var ts = DateTime.Now - start;
155 | //WriteError("无法为{0}连接远程服务器{1}!耗时{2}!{3}", Remote, RemoteServerUri, ts, ex.Message);
156 | WriteError(ex.Message);
157 |
158 | if (session != null) session.Dispose();
159 | Dispose();
160 | }
161 | }
162 | }
163 |
164 | /// 为会话创建与远程服务器通讯的Socket。可以使用Socket池达到重用的目的。
165 | ///
166 | ///
167 | protected virtual ISocketClient CreateRemote(ReceivedEventArgs e)
168 | {
169 | var client = RemoteServerUri.CreateRemote();
170 | //// 如果是Tcp,收到空数据时不要断开。为了稳定可靠,默认设置
171 | //if (client is TcpSession tcp) tcp.DisconnectWhenEmptyData = false;
172 |
173 | return client;
174 | }
175 |
176 | /// 远程连接断开时触发。默认销毁整个会话,子类可根据业务情况决定客户端与代理的链接是否重用。
177 | ///
178 | protected virtual void OnRemoteDispose(ISocketClient client) => Dispose();
179 |
180 | void Remote_Received(Object sender, ReceivedEventArgs e)
181 | {
182 | if (Disposed) return;
183 |
184 | try
185 | {
186 | OnReceiveRemote(e);
187 | }
188 | catch (Exception ex)
189 | {
190 | WriteError(ex.Message);
191 | Dispose();
192 | }
193 | }
194 |
195 | /// 收到远程服务器返回的数据
196 | ///
197 | protected virtual void OnReceiveRemote(ReceivedEventArgs e)
198 | {
199 | if (Disposed) return;
200 |
201 | var len = e.Packet.Total;
202 | if (len > 0 || len == 0 && ExchangeEmptyData)
203 | {
204 | using var span = Host.Tracer?.NewSpan("proxy:OnReceiveRemote", RemoteServerUri + "");
205 | if (len > 0) WriteLogFile("server", e.Packet);
206 |
207 | var session = Session;
208 | if (session == null || session.Disposed)
209 | Dispose();
210 | else
211 | Send(e.Packet);
212 | }
213 | }
214 | #endregion
215 |
216 | #region 发送
217 | /// 发送数据
218 | /// 缓冲区
219 | public virtual Int32 SendToRemote(IPacket pk)
220 | {
221 | var client = RemoteServer;
222 | using var span = Host.Tracer?.NewSpan($"proxy:SendToRemote:{client.Local.Address}", RemoteServerUri + "");
223 | try
224 | {
225 | return RemoteServer.Send(pk);
226 | }
227 | catch (Exception ex)
228 | {
229 | span?.SetError(ex, null);
230 |
231 | Dispose();
232 | throw;
233 | }
234 | }
235 | #endregion
236 |
237 | #region 错误处理
238 | ///
239 | ///
240 | ///
241 | protected override void OnError(Object sender, ExceptionEventArgs e)
242 | {
243 | if (e.Exception != null) Dispose();
244 | }
245 | #endregion
246 |
247 | #region 辅助
248 | private String _LogPrefix;
249 | /// 日志前缀
250 | public override String LogPrefix
251 | {
252 | get
253 | {
254 | if (_LogPrefix == null)
255 | {
256 | var session = this as INetSession;
257 | var name = session.Host == null ? "" : session.Host.Name.TrimEnd("Proxy");
258 | _LogPrefix = $"{name}[{ID}] ";
259 | }
260 | return _LogPrefix;
261 | }
262 | set { _LogPrefix = value; }
263 | }
264 |
265 | /// 已重载。
266 | ///
267 | public override String ToString() => base.ToString() + "=>" + RemoteServerUri;
268 | #endregion
269 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/Socks5/Socks5Client.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace NewLife.Net.Proxy
3 | {
4 | class Socks5Client
5 | {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/XProxy/Proxy/Socks5/Socks5Entity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using NewLife.Serialization;
4 |
5 | namespace NewLife.Net.Proxy
6 | {
7 | /// Socks5实体基类
8 | public abstract class Socks5Entity
9 | {
10 | private Byte _Ver = 5;
11 | /// 版本。默认5
12 | public Byte Ver { get { return _Ver; } set { _Ver = value; } }
13 | }
14 |
15 | /// Socks5请求
16 | public class Socks5Request : Socks5Entity
17 | {
18 | private Byte _Count = 2;
19 | /// 方法数
20 | public Byte Count { get { return _Count; } set { _Count = value; } }
21 |
22 | [FieldSize(255)]
23 | private Byte[] _Methods = new Byte[] { 0, 2 };
24 | /// 方法
25 | public Byte[] Methods { get { return _Methods; } set { _Methods = value; } }
26 |
27 | #region 扩展
28 | const Byte NoAuth = 0;
29 | const Byte Auth = 2;
30 |
31 | /// 是否支持不认证
32 | public Boolean SupportNoAuth
33 | {
34 | get
35 | {
36 | for (int i = 0; i < Count && _Methods != null && i < _Methods.Length; i++)
37 | {
38 | if (_Methods[i] == NoAuth) return true;
39 | }
40 | return false;
41 | }
42 | set
43 | {
44 | for (int i = 0; i < Count && _Methods != null && i < _Methods.Length; i++)
45 | {
46 | if (_Methods[i] == NoAuth) return;
47 | }
48 | if (_Methods == null)
49 | _Methods = new Byte[1];
50 | else if (_Methods.Length <= Count)
51 | {
52 | var ss = new Byte[_Methods.Length + 1];
53 | _Methods.CopyTo(ss, 0);
54 | _Methods = ss;
55 | }
56 | _Methods[Count] = NoAuth;
57 | Count++;
58 | }
59 | }
60 |
61 | /// 是否支持用户名密码认证
62 | public Boolean SupportAuth
63 | {
64 | get
65 | {
66 | for (int i = 0; i < Count && _Methods != null && i < _Methods.Length; i++)
67 | {
68 | if (_Methods[i] == Auth) return true;
69 | }
70 | return false;
71 | }
72 | set
73 | {
74 | for (int i = 0; i < Count && _Methods != null && i < _Methods.Length; i++)
75 | {
76 | if (_Methods[i] == Auth) return;
77 | }
78 | if (_Methods == null)
79 | _Methods = new Byte[1];
80 | else if (_Methods.Length <= Count)
81 | {
82 | var ss = new Byte[_Methods.Length + 1];
83 | _Methods.CopyTo(ss, 0);
84 | _Methods = ss;
85 | }
86 | _Methods[Count] = Auth;
87 | Count++;
88 | }
89 | }
90 | #endregion
91 | }
92 |
93 | /// Socks5答复
94 | public class Socks5Answer : Socks5Entity
95 | {
96 | private Byte _MethodOrStatus;
97 |
98 | /// 方法
99 | public Byte Method { get { return _MethodOrStatus; } set { _MethodOrStatus = value; } }
100 |
101 | /// 认证状态
102 | public Byte Status { get { return _MethodOrStatus; } set { _MethodOrStatus = value; } }
103 | }
104 |
105 | /// Socks5实体
106 | public class Socks5Entity2 : Socks5Entity, IAccessor
107 | {
108 | private Byte _CmdOrRep;
109 |
110 | private Byte _Rsv;
111 | /// 保留
112 | public Byte Rsv { get { return _Rsv; } set { _Rsv = value; } }
113 |
114 | private Socks5AddressType _AddressType = Socks5AddressType.IPv4;
115 | /// 地址类型
116 | public Socks5AddressType AddressType { get { return _AddressType; } set { _AddressType = value; } }
117 |
118 | [NonSerialized]
119 | private String _Address;
120 | /// 地址
121 | public String Address { get { return _Address; } set { _Address = value; } }
122 |
123 | [NonSerialized]
124 | private UInt16 _Port;
125 | /// 端口
126 | public UInt16 Port { get { return _Port; } set { _Port = value; } }
127 |
128 | #region 扩展属性
129 | /// 命令
130 | public Socks5Command Command { get { return (Socks5Command)_CmdOrRep; } set { _CmdOrRep = (Byte)value; } }
131 |
132 | /// 响应
133 | public Socks5Response Response { get { return (Socks5Response)_CmdOrRep; } set { _CmdOrRep = (Byte)value; } }
134 | #endregion
135 |
136 | #region IAccessor 成员
137 | Boolean IAccessor.Read(IReader reader) { return false; }
138 |
139 | Boolean IAccessor.ReadComplete(IReader reader, Boolean success)
140 | {
141 | var rd = reader as IReader2;
142 | switch (AddressType)
143 | {
144 | case Socks5AddressType.IPv4:
145 | Address = new IPAddress(rd.ReadBytes(4)).ToString();
146 | break;
147 | case Socks5AddressType.DomainName:
148 | Address = rd.ReadString();
149 | break;
150 | case Socks5AddressType.IPv6:
151 | Address = new IPAddress(rd.ReadBytes(16)).ToString();
152 | break;
153 | default:
154 | break;
155 | }
156 | Port = rd.ReadUInt16();
157 | return success;
158 | }
159 |
160 | Boolean IAccessor.Write(IWriter writer) { return false; }
161 |
162 | Boolean IAccessor.WriteComplete(IWriter writer, Boolean success)
163 | {
164 | var wr = writer as IWriter2;
165 | switch (AddressType)
166 | {
167 | case Socks5AddressType.IPv4:
168 | wr.Write(IPAddress.Parse(Address).GetAddressBytes());
169 | break;
170 | case Socks5AddressType.DomainName:
171 | wr.Write(Address);
172 | break;
173 | case Socks5AddressType.IPv6:
174 | wr.Write(IPAddress.Parse(Address).GetAddressBytes());
175 | break;
176 | default:
177 | break;
178 | }
179 | wr.Write(Port);
180 | return success;
181 | }
182 | #endregion
183 | }
184 |
185 | /// Socks5命令
186 | public enum Socks5Command : byte
187 | {
188 | /// Connect
189 | Connect = 1,
190 |
191 | /// Bind
192 | Bind = 2,
193 |
194 | /// UdpAssociate
195 | UdpAssociate = 3,
196 | }
197 |
198 | /// Socks5响应类型
199 | public enum Socks5Response : byte
200 | {
201 | /// 成功
202 | Success = 0,
203 |
204 | /// 普通的SOCKS服务器请求失败
205 | GeneralSocksServerFailure,
206 |
207 | /// 现有的规则不允许的连接
208 | ConnectionNotAllowed,
209 |
210 | /// 网络不可达
211 | NetworkUnreachable,
212 |
213 | /// 主机不可达
214 | HostUnreachable,
215 |
216 | /// 连接被拒
217 | ConnectionRefused,
218 |
219 | /// TTL超时
220 | TTLExpired,
221 |
222 | /// 不支持的命令
223 | CommandNotSupported,
224 |
225 | /// 不支持的地址类型
226 | AddressTypeNotSupported,
227 |
228 | /// 未定义错误
229 | UnknownError
230 | }
231 |
232 | /// Socks5地址类型
233 | public enum Socks5AddressType : byte
234 | {
235 | /// IPv4地址
236 | IPv4 = 1,
237 |
238 | /// 域名
239 | DomainName = 3,
240 |
241 | /// IPv6地址
242 | IPv6 = 4
243 | }
244 |
245 | /// Socks5认证消息
246 | public class Socks5Auth : Socks5Entity
247 | {
248 | private String _UserName;
249 | /// 用户名
250 | public String UserName { get { return _UserName; } set { _UserName = value; } }
251 |
252 | private String _Password;
253 | /// 密码
254 | public String Password { get { return _Password; } set { _Password = value; } }
255 | }
256 | }
--------------------------------------------------------------------------------
/XProxy/Proxy/Socks5/Socks5Server.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace NewLife.Net.Proxy
3 | {
4 | /// Socks5代理
5 | ///
6 | /// 1,Socks5Request。协商认证方法
7 | /// 2,Socks5Answer。确定认证方法
8 | /// 3,Socks5Entity2。请求命令
9 | /// 4,Socks5Entity2。响应命令
10 | /// 5,Socks5Auth。请求认证
11 | /// 6,Socks5Answer。响应认证
12 | /// 7,开始传输
13 | ///
14 | public class Socks5 : ProxyBase
15 | {
16 | ///// 创建会话
17 | /////
18 | /////
19 | //protected override INetSession CreateSession(NetEventArgs e)
20 | //{
21 | // return new Session();
22 | //}
23 |
24 | #region 会话
25 | /// Socks5代理会话
26 | public class Session : ProxySession
27 | {
28 | ///// 代理对象
29 | //public new HttpReverseProxy Proxy { get { return base.Proxy as HttpReverseProxy; } set { base.Proxy = value; } }
30 |
31 | ///// 收到客户端发来的数据。子类可通过重载该方法来修改数据
32 | /////
33 | ///// 数据
34 | ///// 修改后的数据
35 | //protected override Stream OnReceive(NetEventArgs e, Stream stream)
36 | //{
37 | // return stream;
38 | //}
39 | }
40 | #endregion
41 | }
42 | }
--------------------------------------------------------------------------------
/XProxy/ProxyHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using NewLife.Net.Proxy;
5 | using NewLife.Reflection;
6 |
7 | namespace XProxy
8 | {
9 | public static class ProxyHelper
10 | {
11 | private static Type[] _proxyArray;
12 | /// 获取所有代理类
13 | ///
14 | public static Type[] GetAll()
15 | {
16 | if (_proxyArray == null) _proxyArray = typeof(ProxyBase).GetAllSubclasses().ToArray();
17 |
18 | return _proxyArray;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/XProxy/Setting.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Xml.Serialization;
3 | using NewLife;
4 | using NewLife.Net.Proxy;
5 | using NewLife.Reflection;
6 | using NewLife.Xml;
7 | using XProxy.Proxy;
8 |
9 | namespace XProxy
10 | {
11 | /// 配置
12 | [XmlConfigFile(@"Config\XProxy.config", 10000)]
13 | class Setting : XmlConfig
14 | {
15 | #region 属性
16 | /// 调试开关。默认true
17 | [Description("调试开关。默认true")]
18 | public Boolean Debug { get; set; } = true;
19 |
20 | /// 配置项
21 | [Description("配置项")]
22 | public ProxyItem[] Items { get; set; }
23 | #endregion
24 |
25 | #region 方法
26 | private static Type[] _proxyArray;
27 | /// 已加载
28 | protected override void OnLoaded()
29 | {
30 | // 本进程的第一次加载时,补齐缺失项
31 | if (_proxyArray == null)
32 | {
33 | var list = new List();
34 |
35 | var ms = Items;
36 | if (ms != null && ms.Length > 0) list.AddRange(ms);
37 |
38 | var demo = list.Count == 0;
39 |
40 | var idx = 1;
41 | var arr = ProxyHelper.GetAll();
42 | foreach (var item in arr)
43 | {
44 | if (!item.IsAbstract && item.GetGenericArguments().Length == 0)
45 | {
46 | var pxy = item.CreateInstance() as ProxyBase;
47 | var name = pxy.Name.TrimEnd("Proxy");
48 | // 如果没有该类型的代理项,则增加一个
49 | if (!list.Any(e => e.Provider == name))
50 | {
51 | var pi = new ProxyItem
52 | {
53 | Name = "Demo" + idx++,
54 | Provider = name,
55 | };
56 |
57 | if (item == typeof(NATProxy))
58 | {
59 | pi.Local = "tcp://0.0.0.0:3388";
60 | pi.Remote = "tcp://localhost:3389";
61 | }
62 | else if (item == typeof(HttpReverseProxy))
63 | {
64 | pi.Local = "tcp://0.0.0.0:8081";
65 | pi.Remote = "tcp://www.newlifex.com:80";
66 | }
67 | else if (item == typeof(HttpProxy))
68 | {
69 | pi.Local = "tcp://0.0.0.0:1080";
70 | pi.Remote = "";
71 | pi.Config = "user=;pass=newlife;realm=Access to NewLife-HttpProxy";
72 | }
73 |
74 | // 演示模式时,默认开启所有代理
75 | if (demo) pi.Enable = true;
76 |
77 | list.Add(pi);
78 | }
79 | }
80 | }
81 |
82 | _proxyArray = arr;
83 |
84 | Items = list.ToArray();
85 | }
86 |
87 | base.OnLoaded();
88 | }
89 |
90 | /// 获取
91 | ///
92 | ///
93 | public ProxyItem Get(String name) => Items.FirstOrDefault(e => e.Name.EqualIgnoreCase(name));
94 |
95 | /// 获取或添加
96 | ///
97 | ///
98 | public ProxyItem GetOrAdd(String name)
99 | {
100 | if (name.IsNullOrEmpty()) return null;
101 |
102 | var mi = Items.FirstOrDefault(e => e.Name.EqualIgnoreCase(name));
103 | if (mi != null) return mi;
104 |
105 | lock (this)
106 | {
107 | var list = new List(Items);
108 | mi = list.FirstOrDefault(e => e.Name.EqualIgnoreCase(name));
109 | if (mi != null) return mi;
110 |
111 | mi = new ProxyItem { Name = name };
112 | list.Add(mi);
113 |
114 | Items = list.ToArray();
115 |
116 | return mi;
117 | }
118 | }
119 | #endregion
120 | }
121 |
122 | /// 代理配置项
123 | public class ProxyItem
124 | {
125 | /// 名称
126 | [XmlAttribute]
127 | public String Name { get; set; }
128 |
129 | /// 代理类型
130 | [XmlAttribute]
131 | public String Provider { get; set; }
132 |
133 | /// 启用
134 | [XmlAttribute]
135 | public Boolean Enable { get; set; }
136 |
137 | /// 本地监听地址和端口
138 | [XmlAttribute]
139 | public String Local { get; set; } = "tcp://0.0.0.0:8080";
140 |
141 | /// 远程地址和端口
142 | [XmlAttribute]
143 | public String Remote { get; set; } = "tcp://www.newlifex.com:80";
144 |
145 | /// 配置字符串
146 | [XmlAttribute]
147 | public String Config { get; set; }
148 | }
149 | }
--------------------------------------------------------------------------------
/XProxy/XProxy.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | XProxy
7 | 新生命代理服务器
8 | NAT反向代理,Http反向代理
9 | 新生命开发团队
10 | 版权所有(C) 新生命开发团队 2002~2025
11 | 3.2
12 | $([System.DateTime]::Now.ToString(`yyyy.MMdd`))
13 | $(VersionPrefix).$(VersionSuffix)
14 | $(Version)
15 | $(VersionPrefix).*
16 | false
17 | ..\Bin\Server
18 | false
19 | latest
20 | enable
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 10.12.2025.401
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/XProxy/leaf.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewLifeX/XProxy/462184c74aa1d090951bf006dccb47f5fef04a4c/XProxy/leaf.ico
--------------------------------------------------------------------------------