Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 6ac655f

Browse files
authored
Merge pull request #83 from jimmystridh/ipaddress-parse-fix
Ipaddress parse fix to handle X-Forwarded-For with ports
2 parents be80953 + 3fc8596 commit 6ac655f

File tree

7 files changed

+232
-22
lines changed

7 files changed

+232
-22
lines changed
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using WebApiThrottle.Net;
7+
using Xunit;
8+
9+
namespace WebApiThrottle.Tests
10+
{
11+
public class IpAddressUtilTests
12+
{
13+
[Fact]
14+
public void IsPrivateIpAddress_PrivateAddress_ReturnsTrue()
15+
{
16+
bool result = IpAddressUtil.IsPrivateIpAddress("10.0.0.1");
17+
18+
Assert.Equal(true, result);
19+
}
20+
21+
[Fact]
22+
public void IsPrivateIpAddress_PublicAddress_ReturnsFalse()
23+
{
24+
bool result = IpAddressUtil.IsPrivateIpAddress("8.8.8.8");
25+
26+
Assert.Equal(false, result);
27+
}
28+
29+
[Fact]
30+
public void IsPrivateIpAddress_PublicAddressIpv6_ReturnsFalse()
31+
{
32+
bool result = IpAddressUtil.IsPrivateIpAddress("2001:4860:4860::8888");
33+
34+
Assert.Equal(false, result);
35+
}
36+
37+
[Fact]
38+
public void IsPrivateIpAddress_PrivateAddressIpv6_ReturnsFalse()
39+
{
40+
bool result = IpAddressUtil.IsPrivateIpAddress("fd74:20cf:81a2::");
41+
42+
Assert.Equal(true, result);
43+
}
44+
45+
[Fact]
46+
public void IsPrivateIpAddress_PrivateAddressWithPort_ReturnsTrue()
47+
{
48+
bool result = IpAddressUtil.IsPrivateIpAddress("10.0.0.1:5555");
49+
50+
Assert.Equal(true, result);
51+
}
52+
53+
[Fact]
54+
public void IsPrivateIpAddress_PrivateAddressIpv6WithPort_ReturnsTrue()
55+
{
56+
bool result = IpAddressUtil.IsPrivateIpAddress("[fd74:20cf:81a2::]:5555");
57+
58+
Assert.Equal(true, result);
59+
}
60+
61+
[Fact]
62+
public void IsPrivateIpAddress_PublicIpAddressWithInitialSpace_ReturnsFalse()
63+
{
64+
bool result = IpAddressUtil.IsPrivateIpAddress(" 8.8.8.8");
65+
66+
Assert.Equal(false, result);
67+
}
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("WebApiThrottle.Tests")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("WebApiThrottle.Tests")]
13+
[assembly: AssemblyCopyright("Copyright © 2016")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("a2db243c-dfd4-4e22-9753-ed95cf6fc21e")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}</ProjectGuid>
8+
<OutputType>Library</OutputType>
9+
<AppDesignerFolder>Properties</AppDesignerFolder>
10+
<RootNamespace>WebApiThrottle.Tests</RootNamespace>
11+
<AssemblyName>WebApiThrottle.Tests</AssemblyName>
12+
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
13+
<FileAlignment>512</FileAlignment>
14+
</PropertyGroup>
15+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16+
<DebugSymbols>true</DebugSymbols>
17+
<DebugType>full</DebugType>
18+
<Optimize>false</Optimize>
19+
<OutputPath>bin\Debug\</OutputPath>
20+
<DefineConstants>DEBUG;TRACE</DefineConstants>
21+
<ErrorReport>prompt</ErrorReport>
22+
<WarningLevel>4</WarningLevel>
23+
</PropertyGroup>
24+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
25+
<DebugType>pdbonly</DebugType>
26+
<Optimize>true</Optimize>
27+
<OutputPath>bin\Release\</OutputPath>
28+
<DefineConstants>TRACE</DefineConstants>
29+
<ErrorReport>prompt</ErrorReport>
30+
<WarningLevel>4</WarningLevel>
31+
</PropertyGroup>
32+
<ItemGroup>
33+
<Reference Include="System" />
34+
<Reference Include="System.Core" />
35+
<Reference Include="System.Xml.Linq" />
36+
<Reference Include="System.Data.DataSetExtensions" />
37+
<Reference Include="Microsoft.CSharp" />
38+
<Reference Include="System.Data" />
39+
<Reference Include="System.Net.Http" />
40+
<Reference Include="System.Xml" />
41+
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
42+
<HintPath>..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
43+
<Private>True</Private>
44+
</Reference>
45+
<Reference Include="xunit.assert, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
46+
<HintPath>..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll</HintPath>
47+
<Private>True</Private>
48+
</Reference>
49+
<Reference Include="xunit.core, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
50+
<HintPath>..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll</HintPath>
51+
<Private>True</Private>
52+
</Reference>
53+
<Reference Include="xunit.execution.desktop, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
54+
<HintPath>..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll</HintPath>
55+
<Private>True</Private>
56+
</Reference>
57+
</ItemGroup>
58+
<ItemGroup>
59+
<Compile Include="IpAddressUtilTests.cs" />
60+
<Compile Include="Properties\AssemblyInfo.cs" />
61+
</ItemGroup>
62+
<ItemGroup>
63+
<None Include="packages.config" />
64+
</ItemGroup>
65+
<ItemGroup>
66+
<ProjectReference Include="..\WebApiThrottle.StrongName\WebApiThrottle.StrongName.csproj">
67+
<Project>{FBF3012B-08EF-408C-9E7D-175ABF286CB4}</Project>
68+
<Name>WebApiThrottle.StrongName</Name>
69+
</ProjectReference>
70+
</ItemGroup>
71+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
72+
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
73+
Other similar extension points exist, see Microsoft.Common.targets.
74+
<Target Name="BeforeBuild">
75+
</Target>
76+
<Target Name="AfterBuild">
77+
</Target>
78+
-->
79+
</Project>

WebApiThrottle.Tests/packages.config

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="xunit" version="2.1.0" targetFramework="net452" />
4+
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
5+
<package id="xunit.assert" version="2.1.0" targetFramework="net452" />
6+
<package id="xunit.core" version="2.1.0" targetFramework="net452" />
7+
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net452" />
8+
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net452" />
9+
</packages>

WebApiThrottle.sln

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiThrottle.WebApiDemo",
1717
EndProject
1818
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiThrottle.StrongName", "WebApiThrottle.StrongName\WebApiThrottle.StrongName.csproj", "{FBF3012B-08EF-408C-9E7D-175ABF286CB4}"
1919
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiThrottle.Tests", "WebApiThrottle.Tests\WebApiThrottle.Tests.csproj", "{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}"
21+
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2224
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
3941
{FBF3012B-08EF-408C-9E7D-175ABF286CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
4042
{FBF3012B-08EF-408C-9E7D-175ABF286CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
4143
{FBF3012B-08EF-408C-9E7D-175ABF286CB4}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{A2DB243C-DFD4-4E22-9753-ED95CF6FC21E}.Release|Any CPU.Build.0 = Release|Any CPU
4248
EndGlobalSection
4349
GlobalSection(SolutionProperties) = preSolution
4450
HideSolutionNode = FALSE

WebApiThrottle/Net/DefaultIpAddressParser.cs

+1-13
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,7 @@ public bool ContainsIp(List<string> ipRules, string clientIp, out string rule)
2020

2121
public virtual IPAddress GetClientIp(HttpRequestMessage request)
2222
{
23-
IPAddress ipAddress;
24-
25-
// use the extension method to get the client ip address as this will
26-
// handle the X-Forward-For header
27-
var ok = IPAddress.TryParse(request.GetClientIpAddress(), out ipAddress);
28-
29-
if (ok)
30-
{
31-
return ipAddress;
32-
}
33-
34-
35-
return null;
23+
return ParseIp(request.GetClientIpAddress());
3624
}
3725

3826
public IPAddress ParseIp(string ipAddress)

WebApiThrottle/Net/IpAddressUtil.cs

+32-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Diagnostics;
24
using System.Linq;
35
using System.Net;
46

@@ -46,6 +48,17 @@ public static bool ContainsIp(List<string> ipRules, string clientIp, out string
4648

4749
public static IPAddress ParseIp(string ipAddress)
4850
{
51+
ipAddress = ipAddress.Trim();
52+
int portDelimiterPos = ipAddress.LastIndexOf(":", StringComparison.InvariantCultureIgnoreCase);
53+
bool ipv6WithPortStart = ipAddress.StartsWith("[");
54+
int ipv6End = ipAddress.IndexOf("]");
55+
if (portDelimiterPos != -1
56+
&& portDelimiterPos == ipAddress.IndexOf(":", StringComparison.InvariantCultureIgnoreCase)
57+
|| ipv6WithPortStart && ipv6End != -1 && ipv6End < portDelimiterPos)
58+
{
59+
ipAddress = ipAddress.Substring(0, portDelimiterPos);
60+
}
61+
4962
return IPAddress.Parse(ipAddress);
5063
}
5164

@@ -58,20 +71,30 @@ public static bool IsPrivateIpAddress(string ipAddress)
5871
// 16-bit block: 192.168.0.0 through 192.168.255.255
5972
// Link-local addresses: 169.254.0.0 through 169.254.255.255 (http://en.wikipedia.org/wiki/Link-local_address)
6073

61-
var ip = IPAddress.Parse(ipAddress);
74+
var ip = ParseIp(ipAddress);
6275
var octets = ip.GetAddressBytes();
6376

64-
var is24BitBlock = octets[0] == 10;
65-
if (is24BitBlock) return true; // Return to prevent further processing
77+
bool isIpv6 = octets.Length == 16;
6678

67-
var is20BitBlock = octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31;
68-
if (is20BitBlock) return true; // Return to prevent further processing
79+
if (isIpv6)
80+
{
81+
bool isUniqueLocalAddress = octets[0] == 253;
82+
return isUniqueLocalAddress;
83+
}
84+
else
85+
{
86+
var is24BitBlock = octets[0] == 10;
87+
if (is24BitBlock) return true; // Return to prevent further processing
88+
89+
var is20BitBlock = octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31;
90+
if (is20BitBlock) return true; // Return to prevent further processing
6991

70-
var is16BitBlock = octets[0] == 192 && octets[1] == 168;
71-
if (is16BitBlock) return true; // Return to prevent further processing
92+
var is16BitBlock = octets[0] == 192 && octets[1] == 168;
93+
if (is16BitBlock) return true; // Return to prevent further processing
7294

73-
var isLinkLocalAddress = octets[0] == 169 && octets[1] == 254;
74-
return isLinkLocalAddress;
95+
var isLinkLocalAddress = octets[0] == 169 && octets[1] == 254;
96+
return isLinkLocalAddress;
97+
}
7598
}
7699
}
77100
}

0 commit comments

Comments
 (0)