diff --git a/gradle.properties b/gradle.properties index 6e8772510..e010b4a16 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx1G mod_version=1.92.0 # Minecraft properties -mc_version=1.16.3 +mc_version=1.16.2 mappings_version=31 # Dependencies diff --git a/patchwork.md b/patchwork.md index 516d00658..4763fe0a9 100644 --- a/patchwork.md +++ b/patchwork.md @@ -52,4 +52,24 @@ were due to unrelated Forge changes. Fix additional `-` in docs Why isn't this automatically stripped! Bad squid. +``` + +``` +275ca58a82c627128a145a8754cbe32568536bd9 +HTTP rules now allow filtering by port + +The HTTP filtering system becomes even more complex! Though in this +case, it's pretty minimal, and definitely worth doing. + +For instance, the following rule will allow connecting to localhost on +port :8080. + + [[http.rules]] + host = "127.0.0.1" + port = 8080 + action = "allow" + + # Other rules as before. + +Closes #540 ``` \ No newline at end of file diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index a5d0ea5e8..68d49e580 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -126,10 +126,10 @@ public final class ComputerCraft implements ModInitializer { public static List buildHttpRulesFromConfig(String[] blacklist, String[] whitelist) { return Stream.concat(Stream.of(blacklist) - .map(x -> AddressRule.parse(x, Action.DENY.toPartial())) + .map( x -> AddressRule.parse( x, null, Action.DENY.toPartial())) .filter(Objects::nonNull), Stream.of(whitelist) - .map(x -> AddressRule.parse(x, Action.ALLOW.toPartial())) + .map( x -> AddressRule.parse( x, null, Action.ALLOW.toPartial())) .filter(Objects::nonNull)) .collect(Collectors.toList()); } diff --git a/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java b/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java index 2ba2b3ea4..7ff1421db 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java +++ b/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java @@ -21,14 +21,14 @@ public class CheckUrl extends Resource { private static final String EVENT = "http_check"; private final IAPIEnvironment environment; private final String address; - private final String host; + private final URI uri; private Future future; public CheckUrl(ResourceGroup limiter, IAPIEnvironment environment, String address, URI uri) { super(limiter); this.environment = environment; this.address = address; - this.host = uri.getHost(); + this.uri = uri; } public void run() { @@ -45,8 +45,9 @@ public class CheckUrl extends Resource { } try { - InetSocketAddress netAddress = NetworkUtils.getAddress(this.host, 80, false); - NetworkUtils.getOptions(this.host, netAddress); + boolean ssl = uri.getScheme().equalsIgnoreCase( "https" ); + InetSocketAddress netAddress = NetworkUtils.getAddress( uri, ssl ); + NetworkUtils.getOptions( uri.getHost(), netAddress ); if (this.tryClose()) { this.environment.queueEvent(EVENT, this.address, true); diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index a91c9a559..5619dbacb 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http; import java.net.InetSocketAddress; +import java.net.URI; import java.security.KeyStore; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; @@ -94,6 +95,21 @@ public final class NetworkUtils { } } + /** + * Create a {@link InetSocketAddress} from a {@link java.net.URI}. + * + * Note, this may require a DNS lookup, and so should not be executed on the main CC thread. + * + * @param uri The URI to fetch. + * @param ssl Whether to connect with SSL. This is used to find the default port if not otherwise specified. + * @return The resolved address. + * @throws HTTPRequestException If the host is not malformed. + */ + public static InetSocketAddress getAddress( URI uri, boolean ssl ) throws HTTPRequestException + { + return getAddress( uri.getHost(), uri.getPort(), ssl ); + } + /** * Create a {@link InetSocketAddress} from the resolved {@code host} and port. * @@ -125,7 +141,7 @@ public final class NetworkUtils { * @throws HTTPRequestException If the host is not permitted */ public static Options getOptions(String host, InetSocketAddress address) throws HTTPRequestException { - Options options = AddressRule.apply(ComputerCraft.httpRules, host, address.getAddress()); + Options options = AddressRule.apply( ComputerCraft.httpRules, host, address ); if (options.action == Action.DENY) { throw new HTTPRequestException("Domain not permitted"); } diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java index 6631193d3..c8d2017b8 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java @@ -8,6 +8,7 @@ package dan200.computercraft.core.apis.http.options; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.regex.Pattern; import javax.annotation.Nonnull; @@ -26,15 +27,22 @@ public final class AddressRule { public static final int WEBSOCKET_MESSAGE = 128 * 1024; private final HostRange ip; private final Pattern domainPattern; + private final Integer port; private final PartialOptions partial; - private AddressRule(@Nullable HostRange ip, @Nullable Pattern domainPattern, @Nonnull PartialOptions partial) { + private AddressRule( + @Nullable HostRange ip, + @Nullable Pattern domainPattern, + @Nullable Integer port, + @Nonnull PartialOptions partial ) + { this.ip = ip; this.domainPattern = domainPattern; this.partial = partial; + this.port = port; } @Nullable - public static AddressRule parse(String filter, @Nonnull PartialOptions partial) { + public static AddressRule parse( String filter, @Nullable Integer port, @Nonnull PartialOptions partial ) { int cidr = filter.indexOf('/'); if (cidr >= 0) { String addressStr = filter.substring(0, cidr); @@ -73,14 +81,14 @@ public final class AddressRule { size -= 8; } - return new AddressRule(new HostRange(minBytes, maxBytes), null, partial); + return new AddressRule(new HostRange(minBytes, maxBytes), null, port, partial); } else { Pattern pattern = Pattern.compile("^\\Q" + filter.replaceAll("\\*", "\\\\E.*\\\\Q") + "\\E$"); - return new AddressRule(null, pattern, partial); + return new AddressRule(null, pattern, port, partial); } } - public static Options apply(Iterable rules, String domain, InetAddress address) { + public static Options apply(Iterable rules, String domain, InetSocketAddress address) { PartialOptions options = null; boolean hasMany = false; @@ -108,11 +116,14 @@ public final class AddressRule { /** * Determine whether the given address matches a series of patterns. * - * @param domain The domain to match - * @param address The address to check. + * @param domain The domain to match + * @param socketAddress The address to check. * @return Whether it matches any of these patterns. */ - private boolean matches(String domain, InetAddress address) { + private boolean matches(String domain, InetSocketAddress socketAddress) { + InetAddress address = socketAddress.getAddress(); + if( port != null && port != socketAddress.getPort() ) return false; + if (this.domainPattern != null) { if (this.domainPattern.matcher(domain) .matches()) { diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java index 8dc11dca0..c520d9675 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java @@ -9,6 +9,8 @@ package dan200.computercraft.core.apis.http.options; public class AddressRuleConfig { // TODO haha config is gone, do fix + // TODO FIGURE OUT WHY THE HELL THE PREVIOUS GUY HAD TO COMMENT THIS OUT + // public static UnmodifiableConfig makeRule( String host, Action action ) // { // CommentedConfig config = InMemoryCommentedFormat.defaultInstance().createConfig( ConcurrentHashMap::new ); @@ -39,17 +41,19 @@ public class AddressRuleConfig { // { // String hostObj = get( builder, "host", String.class ).orElse( null ); // return hostObj != null && checkEnum( builder, "action", Action.class ) + // && check( builder, "port", Number.class ) // && check( builder, "timeout", Number.class ) // && check( builder, "max_upload", Number.class ) // && check( builder, "max_download", Number.class ) // && check( builder, "websocket_message", Number.class ) - // && AddressRule.parse( hostObj, PartialOptions.DEFAULT ) != null; + // && AddressRule.parse( hostObj, port, PartialOptions.DEFAULT ) != null; // } // // @Nullable // public static AddressRule parseRule( UnmodifiableConfig builder ) // { // String hostObj = get( builder, "host", String.class ).orElse( null ); + // Integer port = get( builder, "port", Number.class ).map( Number::intValue ).orElse( null ); // if( hostObj == null ) return null; // // Action action = getEnum( builder, "action", Action.class ).orElse( null ); @@ -66,7 +70,7 @@ public class AddressRuleConfig { // websocketMessage // ); // - // return AddressRule.parse( hostObj, options ); + // return AddressRule.parse( hostObj, port, options ); // } // // private static boolean check( UnmodifiableConfig config, String field, Class klass ) diff --git a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java index 6e6a315f5..7e7645004 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java +++ b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java @@ -131,7 +131,7 @@ public class HttpRequest extends Resource { try { boolean ssl = uri.getScheme() .equalsIgnoreCase("https"); - InetSocketAddress socketAddress = NetworkUtils.getAddress(uri.getHost(), uri.getPort(), ssl); + InetSocketAddress socketAddress = NetworkUtils.getAddress(uri, ssl); Options options = NetworkUtils.getOptions(uri.getHost(), socketAddress); SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null; diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java index 493ac3d99..1014c2e85 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java @@ -117,7 +117,7 @@ public class Websocket extends Resource { boolean ssl = this.uri.getScheme() .equalsIgnoreCase("wss"); - InetSocketAddress socketAddress = NetworkUtils.getAddress(this.uri.getHost(), this.uri.getPort(), ssl); + InetSocketAddress socketAddress = NetworkUtils.getAddress(uri, ssl); Options options = NetworkUtils.getOptions(this.uri.getHost(), socketAddress); SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null; diff --git a/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java b/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java new file mode 100644 index 000000000..690cb37f5 --- /dev/null +++ b/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java @@ -0,0 +1,34 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ + +package dan200.computercraft.core.apis.http.options; + +import org.junit.jupiter.api.Test; + +import java.net.InetSocketAddress; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AddressRuleTest +{ + @Test + public void matchesPort() + { + Iterable rules = Collections.singletonList( AddressRule.parse( + "127.0.0.1", 8080, + new PartialOptions( Action.ALLOW, null, null, null, null ) + ) ); + + assertEquals( apply( rules, "localhost", 8080 ).action, Action.ALLOW ); + assertEquals( apply( rules, "localhost", 8081 ).action, Action.DENY ); + } + + private Options apply( Iterable rules, String host, int port ) + { + return AddressRule.apply( rules, host, new InetSocketAddress( host, port ) ); + } +}