Skip to content

Commit

Permalink
Add parsing of Forwarded header for schemeHostAndPort again. WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
babenhauserheide authored and ArneBab committed May 22, 2022
1 parent 95b91d6 commit f346f45
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
36 changes: 34 additions & 2 deletions src/freenet/clients/http/FProxyToadlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -996,11 +998,41 @@ private String getSchemeHostAndPort(ToadletContext ctx) {

// get uri host and headers
MultiValueTable<String, String> headers = ctx.getHeaders();
// TODO: parse the Forwarded header, too. Skipped here to reduce the scope.
Map<String, String> 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<String, String> parseForwardedHeader(String forwarded) {
if (forwarded == null || forwarded.trim().isEmpty()) {
return new HashMap<>();
}
Map<String, String> 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) {
Expand Down
24 changes: 13 additions & 11 deletions src/freenet/clients/http/utils/UriFilterProxyHeaderParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -17,12 +18,21 @@ public static SchemeAndHostWithPort parse (
Option<?> fProxyBindToConfig,
String uriScheme,
String uriHost,
MultiValueTable<String, String> headers
MultiValueTable<String, String> headers,
Map<String, String> 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<String> safeProtocols = new HashSet<>(Arrays.asList("http", "https"));

List<String> 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
Expand All @@ -36,16 +46,8 @@ public static SchemeAndHostWithPort parse (
Set<String> 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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -287,7 +287,8 @@ private void testUriPrefixMatchesExpected(
fakeBindToOption(fProxyBindTo),
uriScheme,
uriHost,
headers)
headers,
new HashMap<>())
.toString();
assertTrue(
String.format(
Expand Down

0 comments on commit f346f45

Please sign in to comment.