Skip to content

Commit

Permalink
checkerframework
Browse files Browse the repository at this point in the history
  • Loading branch information
danthe1st committed Mar 10, 2024
1 parent 3583137 commit c5c71ea
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 44 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.pem
.secret
intercept.yaml
.classes.jsa

# Created by https://www.toptal.com/developers/gitignore/api/eclipse,maven,java,intellij+all
# Edit at https://www.toptal.com/developers/gitignore?templates=eclipse,maven,java,intellij+all
Expand Down
38 changes: 36 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<slf4j-version>2.0.12</slf4j-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.5.1</logback.version>
<checkerframework.version>3.42.0</checkerframework.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -47,6 +48,12 @@
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>${checkerframework.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand All @@ -63,6 +70,34 @@
<version>3.12.1</version>
<configuration>
<release>21</release>
<fork>true</fork> <!-- Must fork or else JVM arguments are ignored. -->
<annotationProcessorPaths>
<path>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>${checkerframework.version}</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>-J-XX:+AutoCreateSharedArchive</arg>
<arg>-J-XX:SharedArchiveFile=.classes.jsa</arg>
<arg>-Xmaxerrs</arg>
<arg>10000</arg>
<arg>-Xmaxwarns</arg>
<arg>10000</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
Expand Down Expand Up @@ -116,8 +151,7 @@
<version>${native-plugin-version}</version>
<extensions>true</extensions>
<configuration>
<buildArgs combine.self="merge"
combine.children="append">
<buildArgs combine.self="merge" combine.children="append">
<buildArg>-O3</buildArg>
<buildArg>--gc=G1</buildArg>
<buildArg>--strict-image-heap</buildArg>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.github.danthe1st.httpsintercept.rules.PostForwardRule;
import io.github.danthe1st.httpsintercept.rules.PreForwardRule;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

public record Config(
HostMatcherConfig ignoredHosts,
List<PreForwardRule> preForwardRules,
List<PostForwardRule> postForwardRules
) {

public Config(HostMatcherConfig ignoredHosts, List<PreForwardRule> preForwardRules, List<PostForwardRule> postForwardRules) {
public Config(@Nullable HostMatcherConfig ignoredHosts, @Nullable List<PreForwardRule> preForwardRules, @Nullable List<PostForwardRule> postForwardRules) {
if(ignoredHosts == null){
ignoredHosts = new HostMatcherConfig(null, null, null);
}
Expand All @@ -27,7 +30,7 @@ public Config(HostMatcherConfig ignoredHosts, List<PreForwardRule> preForwardRul
this.postForwardRules = emptyIfNull(postForwardRules);
}

private <T> List<T> emptyIfNull(List<T> preForwardRules) {
private <@NonNull T> List<T> emptyIfNull(@UnderInitialization Config this, @Nullable List<T> preForwardRules) {
if(preForwardRules == null){
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import java.util.Collections;
import java.util.Set;

import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.nullness.qual.Nullable;

public record HostMatcherConfig(Set<String> exact,
Set<String> partial,
Set<String> regex) {

public HostMatcherConfig(Set<String> exact, Set<String> partial, Set<String> regex) {
public HostMatcherConfig(@Nullable Set<String> exact, @Nullable Set<String> partial, @Nullable Set<String> regex) {
this.exact = emptyIfNull(exact);
this.partial = emptyIfNull(partial);
this.regex = emptyIfNull(regex);
}

private Set<String> emptyIfNull(Set<String> data) {
private Set<String> emptyIfNull(@UnderInitialization HostMatcherConfig this, @Nullable Set<String> data) {
if(data == null){
return Collections.emptySet();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SniHandler;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -47,7 +48,7 @@ public ServerHandlersInit(Bootstrap clientBootstrap, Config config) throws KeySt
ignoredHostMatcher = new HostMatcher<>(List.of(Map.entry(config.ignoredHosts(), new Object())), false);
}

private <T extends ProcessingRule> HostMatcher<T> createMatcherFromRules(List<T> ruleList) {
private <T extends ProcessingRule> HostMatcher<T> createMatcherFromRules(@UnderInitialization ServerHandlersInit this, List<T> ruleList) {
List<Map.Entry<HostMatcherConfig, T>> rules = new ArrayList<>();
for(T rule : ruleList){
HostMatcherConfig hostMatcher = rule.hostMatcher();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.FullHttpResponse;
import org.bouncycastle.util.Arrays;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

public class HttpResponseContentAccessor {
private ByteBuf contentBuf;
private byte[] bytes;
private byte @Nullable [] bytes;

public HttpResponseContentAccessor(FullHttpResponse res) {
contentBuf = res.content();
Expand All @@ -24,15 +26,18 @@ public String getAsString() {
return new String(bytes, StandardCharsets.UTF_8);
}

@EnsuresNonNull("bytes")
private void ensureBytesPresent() {
if(bytes == null){
byte[] b;
ByteBuf buf = contentBuf.copy();
try{
bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
b = new byte[buf.readableBytes()];
buf.readBytes(b);
}finally{
buf.release();
}
bytes = b;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -18,7 +19,7 @@ public final class RawForwardIncomingRequestHandler extends ChannelInboundHandle

private final String hostname;
private final Bootstrap clientBootstrapTemplate;
private Channel outChannel = null;
private @Nullable Channel outChannel = null;

public RawForwardIncomingRequestHandler(String hostname, Bootstrap clientBootstrapTemplate) {
this.hostname = hostname;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,19 @@ private SNIHandlerMapping() throws KeyStoreException, IOException, NoSuchAlgorit
ks.load(is, passphrase);
}

rootCert = (X509Certificate) ks.getCertificate("root");
if(!(ks.getCertificate("root") instanceof X509Certificate loadedRoot)){
throw new IllegalStateException("No root certificate found");
}

if(!(ks.getKey("root", privateKeyPassword) instanceof PrivateKey rootKey)){
throw new IllegalStateException("No root certificate private key found");
}

rootCert = loadedRoot;

rootKeyPair = new KeyPair(
rootCert.getPublicKey(),
(PrivateKey) ks.getKey("root", privateKeyPassword)
rootKey
);

serverKeyPair = CertificateGenerator.generateKeyPair();
Expand All @@ -94,10 +102,6 @@ private void runCleanupDaemon() {
}
}

private KeyPair extractKeyPair(KeyStore ks, String keyName, char[] passphrase) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
return new KeyPair(ks.getCertificate(keyName).getPublicKey(), (PrivateKey)ks.getKey(keyName, passphrase));
}

@Override
public SslContext map(String hostname) {
LOG.debug("loadding certificate for hostname {}", hostname);
Expand All @@ -114,7 +118,7 @@ public SslContext map(String hostname) {
private SslContext createSslContext(String hostname) {
try{
X509Certificate newCert = CertificateGenerator.createCertificate(serverKeyPair, hostname, rootKeyPair, rootCert, false);
return SslContextBuilder.forServer(serverKeyPair.getPrivate(), (String)null, newCert, rootCert).build();
return SslContextBuilder.forServer(serverKeyPair.getPrivate(), newCert, rootCert).build();
}catch(SSLException | CertIOException | OperatorCreationException | CertificateException e){
throw new CertificateGenerationException("ailed to initialize the server-side SSLContext", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Objects;

import io.netty.handler.ssl.SslContext;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;

class SslContextCacheEntry {
private final SslContext sslContext;
Expand All @@ -18,7 +19,7 @@ public SslContext getSslContext() {
return sslContext;
}

private void refresh() {
private void refresh(@UnknownInitialization SslContextCacheEntry this) {
lastAccessTime = System.currentTimeMillis();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
import java.util.NoSuchElementException;
import java.util.function.Predicate;

final class FilterIterator<T> implements Iterator<T> {
private final Iterator<T> iterator;
private final Predicate<T> filter;
private T current;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

final class FilterIterator<@NonNull T> implements Iterator<T> {
private final Iterator<@NonNull T> iterator;
private final Predicate<@NonNull T> filter;
private @Nullable T current;

public FilterIterator(Iterator<T> iterator, Predicate<T> filter) {
public FilterIterator(Iterator<@NonNull T> iterator, Predicate<@NonNull T> filter) {
this.iterator = iterator;
this.filter = filter;
}
@Override
@EnsuresNonNullIf(expression = "current", result = true)
public boolean hasNext() {
if(current != null){
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@
import java.util.regex.Pattern;

import io.github.danthe1st.httpsintercept.config.HostMatcherConfig;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

public final class HostMatcher<T> {
private final Map<String, List<T>> exactHosts;
private final Map<String, List<T>> hostParts;
private final Map<Pattern, List<T>> hostRegexes;
private final List<T> wildcards;
public final class HostMatcher<@NonNull T> {
private final Map<String, List<@NonNull T>> exactHosts;
private final Map<String, List<@NonNull T>> hostParts;
private final Map<Pattern, List<@NonNull T>> hostRegexes;
private final List<@NonNull T> wildcards;

public HostMatcher(List<Map.Entry<HostMatcherConfig, T>> configs, boolean allowWildcard) {
Map<String, List<T>> hosts = new HashMap<>();
Map<String, List<T>> parts = new HashMap<>();
Map<Pattern, List<T>> regexes = new HashMap<>();
List<T> wildcardElements = new ArrayList<>();
for(Map.Entry<HostMatcherConfig, T> entry : configs){
public HostMatcher(List<Map.Entry<HostMatcherConfig, @NonNull T>> configs, boolean allowWildcard) {
Map<String, List<@NonNull T>> hosts = new HashMap<>();
Map<String, List<@NonNull T>> parts = new HashMap<>();
Map<Pattern, List<@NonNull T>> regexes = new HashMap<>();
List<@NonNull T> wildcardElements = new ArrayList<>();
for(Map.Entry<HostMatcherConfig, @NonNull T> entry : configs){
HostMatcherConfig config = entry.getKey();
T value = entry.getValue();
if(allowWildcard && config.exact().isEmpty() && config.partial().isEmpty() && config.regex().isEmpty()){
Expand All @@ -43,21 +46,21 @@ public HostMatcher(List<Map.Entry<HostMatcherConfig, T>> configs, boolean allowW
this.wildcards = List.copyOf(wildcardElements);
}

private <K> void addToMap(Map<K, List<T>> multimap, T value, Set<String> configValue, Function<String, K> keyTransformer) {
private <K> void addToMap(@UnderInitialization HostMatcher<T> this, Map<K, List<@NonNull T>> multimap, @NonNull T value, Set<String> configValue, Function<String, K> keyTransformer) {
for(String host : configValue){
multimap
.computeIfAbsent(keyTransformer.apply(host), h -> new ArrayList<>())
.computeIfAbsent(keyTransformer.apply(host), h -> new ArrayList<@NonNull T>())
.add(value);
}
}

private <K> Map<K, List<T>> toImmutable(Map<K, List<T>> multimap) {
private <@NonNull K> Map<@NonNull K, List<@NonNull T>> toImmutable(@UnderInitialization HostMatcher<T> this, Map<@NonNull K, List<@NonNull T>> multimap) {
multimap.replaceAll((k, list) -> List.copyOf(list));
return Map.copyOf(multimap);
}

public Iterator<T> allMatches(String hostname) {
Queue<Iterator<T>> iterators = new ArrayDeque<>();
public Iterator<@NonNull T> allMatches(String hostname) {
Queue<Iterator<@NonNull T>> iterators = new ArrayDeque<>();

if(exactHosts.containsKey(hostname)){
iterators.add(exactHosts.get(hostname).iterator());
Expand All @@ -68,11 +71,11 @@ public Iterator<T> allMatches(String hostname) {
iterators.add(new RegexIterator<>(hostRegexes, hostname));
iterators.add(wildcards.iterator());

Iterator<T> it = new IteratingIterator<>() {
private Iterator<T> current = iterators.poll();
Iterator<@NonNull T> it = new IteratingIterator<@NonNull T>() {
private @Nullable Iterator<@NonNull T> current = iterators.poll();

@Override
protected Iterator<T> findNextIterator() {
protected Iterator<@NonNull T> findNextIterator() {
while(current != null && !current.hasNext()){
current = iterators.poll();
}
Expand All @@ -86,7 +89,7 @@ protected Iterator<T> findNextIterator() {
return distinctIterator(it);
}

private Iterator<T> distinctIterator(Iterator<T> it) {
private Iterator<@NonNull T> distinctIterator(Iterator<@NonNull T> it) {
Set<T> matchers = Collections.newSetFromMap(new IdentityHashMap<>());
return new FilterIterator<>(it, element -> {
if(!matchers.contains(element)){
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.danthe1st.httpsintercept.rules;

import io.github.danthe1st.httpsintercept.config.HostMatcherConfig;
import org.checkerframework.checker.nullness.qual.Nullable;

public interface ProcessingRule {
@Nullable
HostMatcherConfig hostMatcher();
}

0 comments on commit c5c71ea

Please sign in to comment.