diff --git a/src/freenet/clients/http/FProxyToadlet.java b/src/freenet/clients/http/FProxyToadlet.java index 5873a386939..f6e49cf1605 100644 --- a/src/freenet/clients/http/FProxyToadlet.java +++ b/src/freenet/clients/http/FProxyToadlet.java @@ -15,8 +15,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import freenet.client.DefaultMIMETypes; @@ -996,11 +998,41 @@ private String getSchemeHostAndPort(ToadletContext ctx) { // get uri host and headers MultiValueTable headers = ctx.getHeaders(); - // TODO: parse the Forwarded header, too. Skipped here to reduce the scope. + Map forwarded = parseForwardedHeader(headers.get("Forwarded")); String uriScheme = ctx.getUri().getScheme(); String uriHost = ctx.getUri().getHost(); - return UriFilterProxyHeaderParser.parse(fProxyPort, fProxyBindTo, uriScheme, uriHost, headers).toString(); + return UriFilterProxyHeaderParser.parse(fProxyPort, fProxyBindTo, uriScheme, uriHost, headers, forwarded).toString(); + } + + private Map parseForwardedHeader(String forwarded) { + if (forwarded == null || forwarded.trim().isEmpty()) { + return new HashMap<>(); + } + Map headerParams = new HashMap<>(); + + // if a multi-value header is given, only use the first value. + int indexOfComma = forwarded.indexOf(','); + if (indexOfComma != -1) { + forwarded = forwarded.substring(0, indexOfComma); + } + boolean hasAtLeastOneKey = forwarded.indexOf('=') != -1; + boolean hasMultipleKeys = forwarded.indexOf(';') != -1; + String[] fields; + if (hasMultipleKeys) { + fields = forwarded.split(";"); + } else if (hasAtLeastOneKey) { + fields = new String[]{ forwarded }; + } else { + return headerParams; + } + for (String field : fields) { + if (field.indexOf('=') != 1) { + String[] keyAndValue = field.split("="); + headerParams.put(keyAndValue[0], keyAndValue[1]); + } + } + return headerParams; } private boolean isBrowser(String ua) { diff --git a/src/freenet/clients/http/utils/UriFilterProxyHeaderParser.java b/src/freenet/clients/http/utils/UriFilterProxyHeaderParser.java index 6b3fe54eb15..50d351e603d 100644 --- a/src/freenet/clients/http/utils/UriFilterProxyHeaderParser.java +++ b/src/freenet/clients/http/utils/UriFilterProxyHeaderParser.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -17,12 +18,21 @@ public static SchemeAndHostWithPort parse ( Option fProxyBindToConfig, String uriScheme, String uriHost, - MultiValueTable headers + MultiValueTable headers, + Map forwarded ) { + String protocol = forwarded.getOrDefault("proto", + headers.containsKey("X-Forwarded-Proto") + ? headers.get("X-Forwarded-Proto") + : uriScheme != null && !uriScheme.trim().isEmpty() ? uriScheme : "http"); + String host = forwarded.getOrDefault("host", + headers.containsKey("X-Forwarded-Host") + ? headers.get("X-Forwarded-Host") + : uriHost != null && !uriHost.trim().isEmpty() ? uriHost : headers.get("host")); Set safeProtocols = new HashSet<>(Arrays.asList("http", "https")); List bindToHosts = Arrays.stream(fProxyBindToConfig.getValueString().split(",")) - .map(host -> host.contains(":") ? "[" + host + "]" : host) + .map(bindToHost -> bindToHost.contains(":") ? "[" + bindToHost + "]" : bindToHost) .collect(Collectors.toList()); String firstBindToHost = bindToHosts.get(0); // set default values @@ -36,16 +46,8 @@ public static SchemeAndHostWithPort parse ( Set safeHosts = new HashSet<>(bindToHosts); // also allow bindTo hosts with the fProxyPortConfig added safeHosts.addAll(safeHosts.stream() - .map(host -> host + ":" + port) + .map(safeHost -> safeHost + ":" + port) .collect(Collectors.toList())); - - // check uri host and headers - String protocol = headers.containsKey("x-forwarded-proto") - ? headers.get("x-forwarded-proto") - : uriScheme != null && !uriScheme.trim().isEmpty() ? uriScheme : "http"; - String host = headers.containsKey("x-forwarded-host") - ? headers.get("x-forwarded-host") - : uriHost != null && !uriHost.trim().isEmpty() ? uriHost : headers.get("host"); // check allow list if (!safeProtocols.contains(protocol)) { protocol = "http"; diff --git a/test/freenet/clients/http/utils/UriFilterProxyHeaderParserTest.java b/test/freenet/clients/http/utils/UriFilterProxyHeaderParserTest.java index bac35e4ca44..814953949d0 100644 --- a/test/freenet/clients/http/utils/UriFilterProxyHeaderParserTest.java +++ b/test/freenet/clients/http/utils/UriFilterProxyHeaderParserTest.java @@ -2,11 +2,11 @@ import static org.junit.Assert.assertTrue; +import java.util.HashMap; + import org.junit.Test; import freenet.config.Config; -import freenet.config.InvalidConfigValueException; -import freenet.config.NodeNeedRestartException; import freenet.config.StringOption; import freenet.support.MultiValueTable; import freenet.support.api.StringCallback; @@ -287,7 +287,8 @@ private void testUriPrefixMatchesExpected( fakeBindToOption(fProxyBindTo), uriScheme, uriHost, - headers) + headers, + new HashMap<>()) .toString(); assertTrue( String.format(