Handle Remote header with ip and port correctly

This commit is contained in:
Pavel Krymets 2015-11-18 10:27:55 -08:00
parent 9910254f3d
commit 8ee803d255
4 changed files with 170 additions and 5 deletions

View File

@ -23,6 +23,7 @@ namespace Microsoft.AspNet.IISPlatformHandler
private const string XForwardedForHeaderName = "X-Forwarded-For";
private const string XForwardedProtoHeaderName = "X-Forwarded-Proto";
private const string XIISWindowsAuthToken = "X-IIS-WindowsAuthToken";
private const string XOriginalPortName = "X-Original-Port";
private const string XOriginalProtoName = "X-Original-Proto";
private const string XOriginalIPName = "X-Original-IP";
@ -83,14 +84,24 @@ namespace Microsoft.AspNet.IISPlatformHandler
if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Length > 0)
{
IPAddress ipFromHeader;
if (IPAddress.TryParse(xForwardedForHeaderValue[0], out ipFromHeader))
int? port;
if (IPAddressWithPortParser.TryParse(xForwardedForHeaderValue[0], out ipFromHeader, out port))
{
var remoteIPString = httpContext.Connection.RemoteIpAddress?.ToString();
var connection = httpContext.Connection;
var remoteIPString = connection.RemoteIpAddress?.ToString();
if (!string.IsNullOrEmpty(remoteIPString))
{
httpContext.Request.Headers[XOriginalIPName] = remoteIPString;
}
httpContext.Connection.RemoteIpAddress = ipFromHeader;
if (port.HasValue)
{
if (connection.RemotePort != 0)
{
httpContext.Request.Headers[XOriginalPortName] = connection.RemotePort.ToString(CultureInfo.InvariantCulture);
}
connection.RemotePort = port.Value;
}
connection.RemoteIpAddress = ipFromHeader;
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Net;
namespace Microsoft.AspNet.IISPlatformHandler
{
public static class IPAddressWithPortParser
{
public static bool TryParse(string addressWithPort, out IPAddress address, out int? port)
{
port = null;
string addressPart = null;
string portPart = null;
var lastColonIndex = addressWithPort.LastIndexOf(':');
if (lastColonIndex > 0)
{
// IPv4 with port or IPv6
var closingIndex = addressWithPort.LastIndexOf(']');
if (closingIndex > 0)
{
// IPv6 with brackets
addressPart = addressWithPort.Substring(1, closingIndex - 1);
if (closingIndex < lastColonIndex)
{
// IPv6 with port [::1]:80
portPart = addressWithPort.Substring(lastColonIndex + 1);
}
}
else
{
// IPv6 without port or IPv4
var firstColonIndex = addressWithPort.IndexOf(':');
if (firstColonIndex != lastColonIndex)
{
// IPv6 ::1
addressPart = addressWithPort;
}
else
{
// IPv4 with port 127.0.0.1:123
addressPart = addressWithPort.Substring(0, firstColonIndex);
portPart = addressWithPort.Substring(firstColonIndex + 1);
}
}
}
else
{
// IPv4 without port
addressPart = addressWithPort;
}
var success = IPAddress.TryParse(addressPart, out address);
if (success && portPart != null)
{
int portValue;
success &= int.TryParse(portPart, out portValue);
if (success)
{
port = portValue;
}
else
{
// we cannot parse port, reset address
address = null;
}
}
return success;
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.IISPlatformHandler
public class HttpPlatformHandlerMiddlewareTests
{
[Fact]
public async Task XForwardedForOverrideChangesRemoteIp()
public async Task XForwardedForOverrideChangesRemoteIpAndPort()
{
var assertsExecuted = false;
@ -22,13 +23,43 @@ namespace Microsoft.AspNet.IISPlatformHandler
app.Run(context =>
{
Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
Assert.Equal(123, context.Connection.RemotePort);
assertsExecuted = true;
return Task.FromResult(0);
});
});
var req = new HttpRequestMessage(HttpMethod.Get, "");
req.Headers.Add("X-Forwarded-For", "11.111.111.11");
req.Headers.Add("X-Forwarded-For", "11.111.111.11:123");
await server.CreateClient().SendAsync(req);
Assert.True(assertsExecuted);
}
[Fact]
public async Task XForwardedForStoresOriginalIpAndPort()
{
var assertsExecuted = false;
var server = TestServer.Create(app =>
{
app.Use((context, next) =>
{
context.Connection.RemoteIpAddress = IPAddress.Loopback;
context.Connection.RemotePort = 1;
return next();
});
app.UseIISPlatformHandler();
app.Run(context =>
{
Assert.Equal("127.0.0.1", context.Request.Headers["X-Original-IP"]);
Assert.Equal("1", context.Request.Headers["X-Original-Port"]);
assertsExecuted = true;
return Task.FromResult(0);
});
});
var req = new HttpRequestMessage(HttpMethod.Get, "");
req.Headers.Add("X-Forwarded-For", "11.111.111.11:123");
await server.CreateClient().SendAsync(req);
Assert.True(assertsExecuted);
}
@ -44,6 +75,7 @@ namespace Microsoft.AspNet.IISPlatformHandler
app.Run(context =>
{
Assert.Null(context.Connection.RemoteIpAddress);
Assert.Equal(0, context.Connection.RemotePort);
assertsExecuted = true;
return Task.FromResult(0);
});

View File

@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Net;
using Microsoft.AspNet.IISPlatformHandler;
using Xunit;
namespace Microsoft.AspNet.PipelineHandler.Tests
{
public class IPAddressWithPortParserTests
{
[Theory]
[InlineData("127.0.0.1", "127.0.0.1", null)]
[InlineData("127.0.0.1:1", "127.0.0.1", 1)]
[InlineData("1", "0.0.0.1", null)]
[InlineData("1:1", "0.0.0.1", 1)]
[InlineData("::1", "::1", null)]
[InlineData("[::1]", "::1", null)]
[InlineData("[::1]:1", "::1", 1)]
public void ParsesCorrectly(string input, string expectedAddress, int? expectedPort)
{
IPAddress address;
int? port;
var success = IPAddressWithPortParser.TryParse(input, out address, out port);
Assert.True(success);
Assert.Equal(expectedAddress, address?.ToString());
Assert.Equal(expectedPort, port);
}
[InlineData("[::1]:")]
[InlineData("[::1:")]
[InlineData("::1:")]
[InlineData("127:")]
[InlineData("127.0.0.1:")]
[InlineData("")]
[InlineData("[]")]
[InlineData("]")]
[InlineData("]:1")]
public void ShouldNotParse(string input)
{
IPAddress address;
int? port;
var success = IPAddressWithPortParser.TryParse(input, out address, out port);
Assert.False(success);
Assert.Equal(null, address);
Assert.Equal(null, port);
}
}
}