-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat]支持ProxyProtocol协议,MqttServer能够获取经过代理之前的真实客户端IP地址,支持nginx/haprox…
…y等反向代理
- Loading branch information
Showing
9 changed files
with
300 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System.Net; | ||
using NewLife.Data; | ||
using NewLife.Model; | ||
using NewLife.Net; | ||
|
||
namespace NewLife.MQTT.ProxyProtocol; | ||
|
||
/// <summary>ProxyProtocol编码器</summary> | ||
public class ProxyCodec : Handler | ||
{ | ||
/// <summary>解析数据包,如果是ProxyProtocol协议则解码后返回</summary> | ||
/// <param name="context"></param> | ||
/// <param name="message"></param> | ||
/// <returns></returns> | ||
public override Object? Read(IHandlerContext context, Object message) | ||
{ | ||
if (message is IPacket pk) | ||
{ | ||
var data = pk.GetSpan(); | ||
if (ProxyMessage.FastValidHeader(data)) | ||
{ | ||
var msg = new ProxyMessage(); | ||
var rs = msg.Read(data); | ||
if (rs > 0) | ||
{ | ||
if (context is IExtend ext) | ||
{ | ||
ext["Proxy"] = msg; | ||
|
||
if (context is NetHandlerContext ctx && ctx.Data is ReceivedEventArgs e) | ||
{ | ||
// 修改远程地址 | ||
e.Remote = msg.GetClient().EndPoint; | ||
} | ||
} | ||
|
||
message = pk.Slice(rs); | ||
} | ||
} | ||
} | ||
|
||
return base.Read(context, message); | ||
} | ||
|
||
/// <summary>编码数据包,加上ProxyProtocol头部</summary> | ||
/// <param name="context"></param> | ||
/// <param name="message"></param> | ||
/// <returns></returns> | ||
public override Object? Write(IHandlerContext context, Object message) | ||
{ | ||
if (message is IPacket pk && context.Owner is ISocketRemote remote) | ||
{ | ||
var msg = new ProxyMessage | ||
{ | ||
Protocol = "TCP4", | ||
ClientIP = remote.Local.Address + "", | ||
ClientPort = remote.Local.Port, | ||
ProxyIP = remote.Remote.Address + "", | ||
ProxyPort = remote.Remote.Port, | ||
}; | ||
|
||
var ap = new ArrayPacket(msg.ToPacket().GetBytes()); | ||
//ap.Append(pk); | ||
ap.Next = pk; | ||
|
||
return base.Write(context, ap); | ||
} | ||
|
||
return base.Write(context, message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
using System.Net; | ||
using System.Text; | ||
using NewLife.Collections; | ||
using NewLife.Data; | ||
using NewLife.Net; | ||
|
||
namespace NewLife.MQTT.ProxyProtocol; | ||
|
||
/// <summary>ProxyProtocol协议消息</summary> | ||
public class ProxyMessage | ||
{ | ||
#region 属性 | ||
/// <summary>内部协议。TCP4等</summary> | ||
public String? Protocol { get; set; } | ||
|
||
/// <summary>客户端IP地址</summary> | ||
public String? ClientIP { get; set; } | ||
|
||
/// <summary>代理IP地址</summary> | ||
public String? ProxyIP { get; set; } | ||
|
||
/// <summary>客户端端口</summary> | ||
public Int32 ClientPort { get; set; } | ||
|
||
/// <summary>代理端口</summary> | ||
public Int32 ProxyPort { get; set; } | ||
#endregion | ||
|
||
#region 核心读写方法 | ||
private static readonly Byte[] _Magic = [(Byte)'P', (Byte)'R', (Byte)'O', (Byte)'X', (Byte)'Y', (Byte)' ']; | ||
private static readonly Byte[] _NewLine = [(Byte)'\r', (Byte)'\n']; | ||
|
||
/// <summary>快速验证协议头</summary> | ||
/// <param name="data"></param> | ||
/// <returns></returns> | ||
public static Boolean FastValidHeader(ReadOnlySpan<Byte> data) => data.StartsWith(_Magic); | ||
|
||
/// <summary>解析协议</summary> | ||
/// <param name="data"></param> | ||
/// <returns></returns> | ||
public Int32 Read(ReadOnlySpan<Byte> data) | ||
{ | ||
if (!data.StartsWith(_Magic)) return -1; | ||
|
||
var p = _Magic.Length; | ||
var p2 = data[p..].IndexOf(_NewLine); | ||
if (p2 <= 0) return -1; | ||
|
||
data = data[..(p + p2)]; | ||
var ss = Encoding.ASCII.GetString(data).Split(' '); | ||
if (ss == null || ss.Length < 6) return -1; | ||
|
||
Protocol = ss[1]; | ||
ClientIP = ss[2]; | ||
ProxyIP = ss[3]; | ||
ClientPort = ss[4].ToInt(); | ||
ProxyPort = ss[5].ToInt(); | ||
|
||
return p + p2 + _NewLine.Length; | ||
} | ||
|
||
/// <summary>转为数据包</summary> | ||
/// <returns></returns> | ||
public String ToPacket() | ||
{ | ||
if (Protocol.IsNullOrEmpty()) Protocol = "TCP4"; | ||
|
||
var sb = Pool.StringBuilder.Get(); | ||
sb.Append("PROXY "); | ||
sb.Append(Protocol); | ||
sb.Append(' '); | ||
sb.Append(ClientIP); | ||
sb.Append(' '); | ||
sb.Append(ProxyIP); | ||
sb.Append(' '); | ||
sb.Append(ClientPort); | ||
sb.Append(' '); | ||
sb.Append(ProxyPort); | ||
sb.Append("\r\n"); | ||
|
||
return sb.Return(true); | ||
} | ||
|
||
/// <summary>获取客户端结点</summary> | ||
/// <returns></returns> | ||
public NetUri GetClient() | ||
{ | ||
var uri = new NetUri | ||
{ | ||
Host = ClientIP, | ||
Port = ClientPort | ||
}; | ||
|
||
return uri; | ||
} | ||
#endregion | ||
} |
Oops, something went wrong.