From 07886a972265bc2a2de9b85c2cc76ad8d0b1f97a Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Tue, 9 Mar 2021 16:05:08 -0500 Subject: [PATCH] Introduce a utility class for working with forwarding chains in HTTP headers. --- .../textsecuregcm/util/ForwardedIpUtil.java | 33 +++++++++++++++++ .../util/ForwardedIpUtilTest.java | 35 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtil.java create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtilTest.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtil.java b/service/src/main/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtil.java new file mode 100644 index 000000000..1dadb2ec6 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtil.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.util; + +import org.apache.commons.lang3.StringUtils; +import java.util.Optional; + +/** + * Tools for working with chains of IP addresses in forwarding lists in HTTP headers. + * + * @see X-Forwarded-For - HTTP | MDN + */ +public class ForwardedIpUtil { + + /** + * Returns the most recent proxy in a chain described by an {@code X-Forwarded-For} header. + * + * @param forwardedFor the value of an X-Forwarded-For header + * + * @return the IP address of the most recent proxy in the forwarding chain, or empty if none was found or + * {@code forwardedFor} was null + */ + public static Optional getMostRecentProxy(final String forwardedFor) { + return Optional.ofNullable(forwardedFor) + .filter(StringUtils::isNotBlank) + .map(proxies -> proxies.split(",")) + .map(proxyArray -> proxyArray[proxyArray.length - 1]) + .map(String::trim); + } +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtilTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtilTest.java new file mode 100644 index 000000000..6c5a8a71f --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/util/ForwardedIpUtilTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.Optional; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ForwardedIpUtilTest { + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + @ParameterizedTest + @MethodSource("argumentsForGetMostRecentProxy") + void getMostRecentProxy(final String forwardedFor, final Optional expectedMostRecentProxy) { + assertEquals(expectedMostRecentProxy, ForwardedIpUtil.getMostRecentProxy(forwardedFor)); + } + + private static Stream argumentsForGetMostRecentProxy() { + return Stream.of( + arguments(null, Optional.empty()), + arguments("", Optional.empty()), + arguments(" ", Optional.empty()), + arguments("203.0.113.195", Optional.of("203.0.113.195")), + arguments("203.0.113.195, 70.41.3.18, 150.172.238.178", Optional.of("150.172.238.178")) + ); + } +}