Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 9 additions & 15 deletions http-server/src/main/java/com/proofpoint/http/server/CidrSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import com.google.common.collect.ImmutableSet;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -27,9 +26,9 @@

public class CidrSet
{
private final Set<Inet4Network> cidrs;
private final Set<InetNetwork> cidrs;

private CidrSet(Collection<Inet4Network> cidrs) {
private CidrSet(Collection<InetNetwork> cidrs) {
this.cidrs = Set.copyOf(cidrs);
}

Expand All @@ -40,8 +39,8 @@ private CidrSet(Collection<Inet4Network> cidrs) {
* @return A {@link CidrSet} identifying all addresses in the blocks in {@code cidrList}.
*/
public static CidrSet fromString(String cidrList) {
Set<Inet4Network> uris = Arrays.stream(cidrList.split("\\s*,\\s*"))
.map(Inet4Network::fromCidr)
Set<InetNetwork> uris = Arrays.stream(cidrList.split("\\s*,\\s*"))
.map(InetNetwork::fromCidr)
.collect(ImmutableSet.toImmutableSet());
return new CidrSet(uris);
}
Expand All @@ -62,13 +61,9 @@ public static CidrSet empty()
*/
public boolean containsAddress(InetAddress address)
{
if (!(address instanceof Inet4Address)) {
return false;
}

Inet4Address inet4Address = (Inet4Address) address;
for (Inet4Network cidr : cidrs) {
if (cidr.containsAddress(inet4Address)) {
InetAddress inetAddress = address;
for (InetNetwork cidr : cidrs) {
if (cidr.containsAddress(inetAddress)) {
return true;
}
}
Expand All @@ -77,7 +72,7 @@ public boolean containsAddress(InetAddress address)
}

public CidrSet union(CidrSet other) {
return new CidrSet(ImmutableSet.<Inet4Network>builder()
return new CidrSet(ImmutableSet.<InetNetwork>builder()
.addAll(cidrs)
.addAll(other.cidrs)
.build());
Expand All @@ -99,15 +94,14 @@ public boolean equals(Object o)
@Override
public int hashCode()
{

return Objects.hash(cidrs);
}

@Override
public String toString()
{
return cidrs.stream()
.map(Inet4Network::toString)
.map(InetNetwork::toString)
.collect(Collectors.joining(","));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import jakarta.servlet.http.HttpServletRequest;

import java.util.Enumeration;
import java.util.stream.StreamSupport;

public class ClientAddressExtractor
{
Expand Down Expand Up @@ -52,7 +53,15 @@ public String clientAddressFor(HttpServletRequest request)
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (Enumeration<String> e = request.getHeaders("X-FORWARDED-FOR"); e != null && e.hasMoreElements(); ) {
String forwardedFor = e.nextElement();
builder.addAll(Splitter.on(',').trimResults().omitEmptyStrings().split(forwardedFor));
StreamSupport.stream(Splitter.on(',').trimResults().omitEmptyStrings().split(forwardedFor).spliterator(), false)
.map(s -> {
if (s.startsWith("[") && s.endsWith("]")) {
return s.substring(1, s.length() - 1).trim();
} else {
return s;
}
})
.forEach(builder::add);
}
if (request.getRemoteAddr() != null) {
builder.add(request.getRemoteAddr());
Expand Down
107 changes: 0 additions & 107 deletions http-server/src/main/java/com/proofpoint/http/server/Inet4Network.java

This file was deleted.

106 changes: 106 additions & 0 deletions http-server/src/main/java/com/proofpoint/http/server/InetNetwork.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2010 Proofpoint, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.proofpoint.http.server;

import com.google.common.net.InetAddresses;

import java.math.BigInteger;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Objects;

import static com.google.common.net.InetAddresses.toBigInteger;

final class InetNetwork
{
private final boolean isIpv6;
private final int bits;
private final BigInteger start;
private final BigInteger end;

private InetNetwork(InetAddress address, int bits)
{
this.isIpv6 = address instanceof Inet6Address;
this.bits = bits;
this.start = toBigInteger(address);
int totalBits = isIpv6 ? 128 : 32;
BigInteger length = BigInteger.valueOf(1).shiftLeft(totalBits - this.bits);
this.end = start.add(length).subtract(BigInteger.valueOf(1));
}

public boolean containsAddress(InetAddress address)
{
BigInteger ip = addressToBigInteger(address);
if ((address instanceof Inet6Address) != isIpv6) {
return false;
}
return (ip.compareTo(start) >= 0) && (ip.compareTo(end) <= 0);
}

@Override
public String toString()
{
if (isIpv6) {
return InetAddresses.toAddrString(InetAddresses.fromIPv6BigInteger(start)) + "/" + bits;
}
return InetAddresses.fromIPv4BigInteger(start).getHostAddress() + "/" + bits;
}

@Override
public boolean equals(Object o)
{
if (o == null || getClass() != o.getClass()) {
return false;
}
InetNetwork that = (InetNetwork) o;
return isIpv6 == that.isIpv6 && bits == that.bits && Objects.equals(start, that.start) && Objects.equals(end, that.end);
}

@Override
public int hashCode()
{
return Objects.hash(isIpv6, bits, start, end);
}

@SuppressWarnings("StringSplitter")
public static InetNetwork fromCidr(String cidr)
{
String[] parts = cidr.split("/");
if (parts.length != 2) {
throw new IllegalArgumentException("invalid CIDR format: " + cidr);
}

InetAddress address = InetAddresses.forString(parts[0]);
int bits = Integer.parseInt(parts[1]);
int totalBits = address instanceof Inet6Address ? 128 : 32;
if ((bits < 0) || (bits > totalBits)) {
throw new IllegalArgumentException("invalid prefix size: " + bits);
}

BigInteger mask = (bits == 0) ? BigInteger.ZERO : (BigInteger.valueOf(-1).shiftLeft(totalBits - bits));
BigInteger ip = InetAddresses.toBigInteger(address);
if (!ip.and(mask).equals(ip)) {
throw new IllegalArgumentException("invalid prefix for prefix size: " + bits);
}

return new InetNetwork(address, bits);
}

static BigInteger addressToBigInteger(InetAddress address)
{
return toBigInteger(address);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

public class TestClientAddressExtractor
{
public static final InternalNetworkConfig INTERNAL_NETWORK_CONFIG = new InternalNetworkConfig().setInternalNetworks(CidrSet.fromString("2001:db8::/64"));
Request request;

@BeforeMethod
Expand All @@ -55,6 +56,15 @@ public void testUseForwardedFor()
assertEquals(new ClientAddressExtractor().clientAddressFor(request), "4.4.4.4");
}

@Test
public void testUseForwardedForIPv6()
{
when(request.getRemoteAddr()).thenReturn("2001:db8::1");
when(request.getHeaders("X-FORWARDED-FOR")).thenReturn(Collections.enumeration(List.of("1.1.1.1, 2.2.2.2", "3.3.3.3, 4.4.4.4")));

assertEquals(new ClientAddressExtractor(INTERNAL_NETWORK_CONFIG).clientAddressFor(request), "4.4.4.4");
}

@Test
public void testUseForwardedForTwoHops()
{
Expand All @@ -64,6 +74,42 @@ public void testUseForwardedForTwoHops()
assertEquals(new ClientAddressExtractor().clientAddressFor(request), "3.3.3.3");
}

@Test
public void testUseForwardedForTwoHopsIPv6ToIPv4()
{
when(request.getRemoteAddr()).thenReturn("2001:db8::1");
when(request.getHeaders("X-FORWARDED-FOR")).thenReturn(Collections.enumeration(List.of("1.1.1.1, 2.2.2.2", "3.3.3.3, 2001:db8::2")));

assertEquals(new ClientAddressExtractor(INTERNAL_NETWORK_CONFIG).clientAddressFor(request), "3.3.3.3");
}

@Test
public void testUseForwardedForTwoHopsBracketedIPv6ToIPv4()
{
when(request.getRemoteAddr()).thenReturn("2001:db8::1");
when(request.getHeaders("X-FORWARDED-FOR")).thenReturn(Collections.enumeration(List.of("1.1.1.1, 2.2.2.2", "3.3.3.3, [2001:db8::2]")));

assertEquals(new ClientAddressExtractor(INTERNAL_NETWORK_CONFIG).clientAddressFor(request), "3.3.3.3");
}

@Test
public void testUseForwardedForTwoHopsIPv6toIPv6()
{
when(request.getRemoteAddr()).thenReturn("2001:db8::1");
when(request.getHeaders("X-FORWARDED-FOR")).thenReturn(Collections.enumeration(List.of("1.1.1.1, 2.2.2.2", "2001:db8:3::3, 2001:db8::2")));

assertEquals(new ClientAddressExtractor(INTERNAL_NETWORK_CONFIG).clientAddressFor(request), "2001:db8:3::3");
}

@Test
public void testUseForwardedForTwoHopsBracketedIPv6toIPv6()
{
when(request.getRemoteAddr()).thenReturn("2001:db8::1");
when(request.getHeaders("X-FORWARDED-FOR")).thenReturn(Collections.enumeration(List.of("1.1.1.1, 2.2.2.2", "[2001:db8:3::3], [2001:db8::2]")));

assertEquals(new ClientAddressExtractor(INTERNAL_NETWORK_CONFIG).clientAddressFor(request), "2001:db8:3::3");
}

@Test
public void testUseForwardedForThreeHops()
{
Expand Down
Loading
Loading