Skip to content

Commit a75f6e2

Browse files
slick-nicNic Colledge
andauthored
Added support for Proxy Protocol (#72)
* Added support for proxy protocol Other SMTP servers support proxy protocol [1] allowing load balancers like HAProxy to tell the server the "real" address of the client making the request. [1] http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt * Added (missing) new properties to interface. * Megred with default for latest updates. Fixed a few changes in the branch code resulting from merge * Update to store proxy protocol addresses the same way as Local / Remote Endpoints are stored since the merge. * Removed now redundant unit test Co-authored-by: Nic Colledge <nic@imperialmetric.net>
1 parent d06d87e commit a75f6e2

File tree

8 files changed

+315
-11
lines changed

8 files changed

+315
-11
lines changed

Src/SmtpServer.Tests/SmtpParserTests.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Net;
34
using System.Text;
45
using SmtpServer.Mail;
56
using SmtpServer.Protocol;
@@ -245,6 +246,42 @@ public void CanMakeRcpt(string email, string user, string host)
245246
Assert.Equal(host, ((RcptCommand)command).Address.Host);
246247
}
247248

249+
[Fact]
250+
public void CanMakeProxyIpV4()
251+
{
252+
// arrange
253+
var parser = CreateParser("PROXY TCP4 192.168.1.1 192.168.1.2 1234 16789");
254+
255+
// act
256+
var result = parser.TryMakeProxy(out SmtpCommand command, out SmtpResponse errorResponse);
257+
258+
// assert
259+
Assert.True(result);
260+
Assert.True(command is ProxyProtocolCommand);
261+
Assert.Equal("192.168.1.1", ((ProxyProtocolCommand)command).SourceEndpoint.Address.ToString());
262+
Assert.Equal("192.168.1.2", ((ProxyProtocolCommand)command).DestinationEndpoint.Address.ToString());
263+
Assert.Equal(1234, ((ProxyProtocolCommand)command).SourceEndpoint.Port);
264+
Assert.Equal(16789, ((ProxyProtocolCommand)command).DestinationEndpoint.Port);
265+
}
266+
267+
[Fact]
268+
public void CanMakeProxyIpV6()
269+
{
270+
// arrange
271+
var parser = CreateParser("PROXY TCP6 2001:1234:abcd::0001 3456:2e76:66d8:f84:abcd:abef:ffff:1234 1234 16789");
272+
273+
// act
274+
var result = parser.TryMakeProxy(out SmtpCommand command, out SmtpResponse errorResponse);
275+
276+
// assert
277+
Assert.True(result);
278+
Assert.True(command is ProxyProtocolCommand);
279+
Assert.Equal(IPAddress.Parse("2001:1234:abcd::0001").ToString(), ((ProxyProtocolCommand)command).SourceEndpoint.Address.ToString());
280+
Assert.Equal(IPAddress.Parse("3456:2e76:66d8:f84:abcd:abef:ffff:1234").ToString(), ((ProxyProtocolCommand)command).DestinationEndpoint.Address.ToString());
281+
Assert.Equal(1234, ((ProxyProtocolCommand)command).SourceEndpoint.Port);
282+
Assert.Equal(16789, ((ProxyProtocolCommand)command).DestinationEndpoint.Port);
283+
}
284+
248285
[Fact]
249286
public void CanMakeAtom()
250287
{
@@ -578,7 +615,7 @@ public void CanMakeIpv6AddressLiteral(string input)
578615
var parser = CreateParser("IPv6:" + input);
579616

580617
// act
581-
var result = parser.TryMakeIpv6AddressLiteral(out var address);
618+
var result = parser.TryMakeIpv6AddressLiteralWithPrefix(out var address);
582619

583620
// assert
584621
Assert.True(result);
@@ -597,7 +634,7 @@ public void CanNotMakeIpv6AddressLiteral(string input)
597634
var parser = CreateParser("IPv6:" + input);
598635

599636
// act
600-
var result = parser.TryMakeIpv6AddressLiteral(out var address);
637+
var result = parser.TryMakeIpv6AddressLiteralWithPrefix(out var address);
601638

602639
// assert
603640
Assert.False(result);

Src/SmtpServer/ISessionContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Net;
34
using SmtpServer.IO;
45

56
namespace SmtpServer
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace SmtpServer.Protocol
7+
{
8+
/// <summary>
9+
/// Support for proxy protocol version 1 header for use with HAProxy.
10+
/// Documented at http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
11+
/// This should always (and only ever) be the first command seen on a new connection from HAProxy
12+
/// </summary>
13+
public sealed class ProxyProtocolCommand : SmtpCommand
14+
{
15+
public const string ProxySourceEndpointKey = "ProxyProtocol:ProxySourceEndpoint";
16+
public const string ProxyDestinationEndpointKey = "ProxyProtocol:ProxyDestinationEndpoint";
17+
18+
public const string Command = "PROXY";
19+
20+
public IPEndPoint SourceEndpoint { get; }
21+
public IPEndPoint DestinationEndpoint { get; }
22+
23+
public ProxyProtocolCommand(ISmtpServerOptions options, IPEndPoint sourceEndpoint, IPEndPoint destinationEndpoint) : base(options)
24+
{
25+
SourceEndpoint = sourceEndpoint;
26+
DestinationEndpoint = destinationEndpoint;
27+
}
28+
29+
internal override Task<bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken)
30+
{
31+
context.Properties.Add(ProxySourceEndpointKey, SourceEndpoint);
32+
context.Properties.Add(ProxyDestinationEndpointKey, DestinationEndpoint);
33+
34+
// Do not transition smtp protocol state for these commands.
35+
return Task.FromResult(false);
36+
}
37+
}
38+
}

Src/SmtpServer/Protocol/SmtpCommandVisitor.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ public void Visit(SmtpCommand command)
4646
return;
4747
}
4848

49+
if (command is ProxyProtocolCommand)
50+
{
51+
Visit((ProxyProtocolCommand)command);
52+
return;
53+
}
54+
4955
if (command is QuitCommand)
5056
{
5157
Visit((QuitCommand)command);
@@ -109,6 +115,13 @@ protected virtual void Visit(MailCommand command) { }
109115
/// <param name="command">The command that is being visited.</param>
110116
protected virtual void Visit(NoopCommand command) { }
111117

118+
/// <summary>
119+
/// Visit an PROXY command.
120+
/// </summary>
121+
/// <param name="command">The command that is being visited.</param>
122+
protected virtual void Visit(ProxyProtocolCommand command) { }
123+
124+
112125
/// <summary>
113126
/// Visit an QUIT command.
114127
/// </summary>

0 commit comments

Comments
 (0)