Introduce an ASN-to-IP manager.

This commit is contained in:
Jon Chambers
2021-05-17 18:40:07 -04:00
committed by Jon Chambers
parent 1160af9522
commit f8c623074b
8 changed files with 171 additions and 10 deletions

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
import static com.codahale.metrics.MetricRegistry.name;
import com.amazonaws.services.s3.model.S3Object;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.lifecycle.Managed;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
public class AsnManager implements Managed {
private final S3ObjectMonitor asnTableMonitor;
private final AtomicReference<AsnTable> asnTable = new AtomicReference<>(AsnTable.EMPTY);
private static final Timer REFRESH_TIMER = Metrics.timer(name(AsnManager.class, "refresh"));
private static final Counter REFRESH_ERRORS = Metrics.counter(name(AsnManager.class, "refreshErrors"));
private static final Logger log = LoggerFactory.getLogger(AsnManager.class);
public AsnManager(
final ScheduledExecutorService scheduledExecutorService,
final MonitoredS3ObjectConfiguration configuration) {
this.asnTableMonitor = new S3ObjectMonitor(
configuration.getS3Region(),
configuration.getS3Bucket(),
configuration.getObjectKey(),
configuration.getMaxSize(),
scheduledExecutorService,
configuration.getRefreshInterval(),
this::handleAsnTableChanged);
}
@Override
public void start() throws Exception {
try {
handleAsnTableChanged(asnTableMonitor.getObject());
} catch (final Exception e) {
log.warn("Failed to load initial IP-to-ASN map", e);
}
asnTableMonitor.start();
}
@Override
public void stop() throws Exception {
asnTableMonitor.stop();
}
public Optional<Long> getAsn(final String address) {
try {
return asnTable.get().getAsn((Inet4Address) Inet4Address.getByName(address));
} catch (final UnknownHostException e) {
log.warn("Could not parse \"{}\" as an Inet4Address", address);
return Optional.empty();
}
}
private void handleAsnTableChanged(final S3Object asnTableObject) {
REFRESH_TIMER.record(() -> {
try {
handleAsnTableChanged(new GZIPInputStream(asnTableObject.getObjectContent()));
} catch (final IOException e) {
log.error("Retrieved object was not a gzip archive", e);
}
});
}
@VisibleForTesting
void handleAsnTableChanged(final InputStream inputStream) {
try (final InputStreamReader reader = new InputStreamReader(inputStream)) {
asnTable.set(new AsnTable(reader));
} catch (final Exception e) {
REFRESH_ERRORS.increment();
log.warn("Failed to refresh IP-to-ASN table", e);
}
}
}

View File

@@ -44,6 +44,8 @@ class AsnTable {
}
}
public static final AsnTable EMPTY = new AsnTable();
public AsnTable(final Reader tsvReader) throws IOException {
final TreeMap<Long, AsnRange> treeMap = new TreeMap<>();
@@ -60,6 +62,10 @@ class AsnTable {
asnBlocksByFirstIp = treeMap;
}
private AsnTable() {
asnBlocksByFirstIp = new TreeMap<>();
}
public Optional<Long> getAsn(final Inet4Address address) {
final long addressAsLong = ipToLong(address);

View File

@@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.configuration.TorExitNodeConfiguration;
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
/**
* A utility for checking whether IP addresses belong to Tor exit nodes using the "bulk exit list."
@@ -44,7 +44,7 @@ public class TorExitNodeManager implements Managed {
public TorExitNodeManager(
final ScheduledExecutorService scheduledExecutorService,
final TorExitNodeConfiguration configuration) {
final MonitoredS3ObjectConfiguration configuration) {
this.exitListMonitor = new S3ObjectMonitor(
configuration.getS3Region(),