├── .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 | ![Demo](Doc/Demo.png) 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 --------------------------------------------------------------------------------