From ace2f3de5d7aca1c4521c30303ce5f86352b0912 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Fri, 21 Jan 2022 18:42:28 +0200 Subject: [PATCH] #280 if scanning a local network, then ARP will be used in addition to chosen Pinger to detect more hosts --- CHANGELOG | 1 + src/net/azib/ipscan/core/Scanner.java | 5 ++-- .../ipscan/core/ScannerDispatcherThread.java | 3 +- src/net/azib/ipscan/core/net/ARPPinger.java | 18 +++++++---- .../azib/ipscan/core/net/PingerRegistry.java | 7 +++-- .../azib/ipscan/feeders/AbstractFeeder.java | 30 ++++++++++++++++--- src/net/azib/ipscan/feeders/Feeder.java | 13 ++++++++ src/net/azib/ipscan/feeders/RandomFeeder.java | 10 +++---- src/net/azib/ipscan/feeders/RangeFeeder.java | 17 ++++------- src/net/azib/ipscan/feeders/RescanFeeder.java | 28 ++++++++--------- src/net/azib/ipscan/fetchers/Fetcher.java | 13 +++++--- src/net/azib/ipscan/fetchers/PingFetcher.java | 5 ++-- .../gui/actions/StartStopScanningAction.java | 4 +-- test/net/azib/ipscan/core/ScannerTest.java | 7 +++-- .../ipscan/core/net/PingerRegistryTest.java | 2 +- .../azib/ipscan/feeders/RescanFeederTest.java | 11 +++---- 16 files changed, 108 insertions(+), 66 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0e61f653c..5fd10fd91 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ Unreleased: - Windows 32-bit build has been removed - Linux deb & rpm packages now correctly specify Java 11 dependency, not 8 - Mac: display a notification if java not in PATH #279 +- If scanning a local network, then ARP will be used in addition to chosen Pinger to detect more hosts #280 Changes in 3.8.1: - Make 32-bit Windows build still run under Oracle Java 8 - it seems to still be popular #324 diff --git a/src/net/azib/ipscan/core/Scanner.java b/src/net/azib/ipscan/core/Scanner.java index 388f91f7a..5e5b6688b 100644 --- a/src/net/azib/ipscan/core/Scanner.java +++ b/src/net/azib/ipscan/core/Scanner.java @@ -7,6 +7,7 @@ import net.azib.ipscan.core.values.NotAvailable; import net.azib.ipscan.core.values.NotScanned; +import net.azib.ipscan.feeders.Feeder; import net.azib.ipscan.fetchers.Fetcher; import net.azib.ipscan.fetchers.FetcherRegistry; @@ -72,9 +73,9 @@ public void interrupt(Thread thread) { /** * Init everything needed for scanning, including Fetchers */ - public void init() { + public void init(Feeder feeder) { for (Fetcher fetcher : fetcherRegistry.getSelectedFetchers()) { - fetcher.init(); + fetcher.init(feeder); } } diff --git a/src/net/azib/ipscan/core/ScannerDispatcherThread.java b/src/net/azib/ipscan/core/ScannerDispatcherThread.java index 296802461..0ea216230 100644 --- a/src/net/azib/ipscan/core/ScannerDispatcherThread.java +++ b/src/net/azib/ipscan/core/ScannerDispatcherThread.java @@ -63,9 +63,8 @@ public ScannerDispatcherThread(Feeder feeder, Scanner scanner, StateMachine stat this.scanningResultList = scanningResults; try { this.scanningResultList.initNewScan(feeder); - // initialize in the main thread in order to catch exceptions gracefully - scanner.init(); + scanner.init(feeder); } catch (RuntimeException e) { stateMachine.reset(); diff --git a/src/net/azib/ipscan/core/net/ARPPinger.java b/src/net/azib/ipscan/core/net/ARPPinger.java index bd00d5778..01c5813c3 100644 --- a/src/net/azib/ipscan/core/net/ARPPinger.java +++ b/src/net/azib/ipscan/core/net/ARPPinger.java @@ -9,19 +9,27 @@ public class ARPPinger implements Pinger { private MACFetcher macFetcher; - private JavaPinger trigger; + private Pinger trigger; public ARPPinger(MACFetcher macFetcher, JavaPinger trigger) { - this.macFetcher = macFetcher; // WinMACFetcher sends an actual ARP request, so no previous UDP request is needed - this.trigger = macFetcher.getClass().getSimpleName().startsWith("Win") ? null : trigger; + this(macFetcher, macFetcher.getClass().getSimpleName().startsWith("Win") ? null : (Pinger) trigger); + } + + public ARPPinger(MACFetcher macFetcher, Pinger trigger) { + this.macFetcher = macFetcher; + this.trigger = trigger; } @Override public PingResult ping(ScanningSubject subject, int count) throws IOException { - PingResult result = new PingResult(subject.getAddress(), 1); + PingResult result = new PingResult(subject.getAddress(), count); + if (trigger != null) count /= 2; for (int i = 0; i < count; i++) { long start = currentTimeMillis(); - if (trigger != null) trigger.ping(subject, 1); // this should issue an ARP request for the IP + if (trigger != null) { + // this should issue an ARP request for the IP + result.merge(trigger.ping(subject, count / 2)); + } String mac = macFetcher.scan(subject); if (mac != null) result.addReply(currentTimeMillis() - start); } diff --git a/src/net/azib/ipscan/core/net/PingerRegistry.java b/src/net/azib/ipscan/core/net/PingerRegistry.java index b304547a2..b3a8e0251 100644 --- a/src/net/azib/ipscan/core/net/PingerRegistry.java +++ b/src/net/azib/ipscan/core/net/PingerRegistry.java @@ -12,6 +12,7 @@ import net.azib.ipscan.di.InjectException; import net.azib.ipscan.di.Injector; import net.azib.ipscan.fetchers.FetcherException; +import net.azib.ipscan.fetchers.MACFetcher; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -60,8 +61,10 @@ public String[] getRegisteredNames() { /** * Creates the configured pinger with configured timeout */ - public Pinger createPinger() throws FetcherException { - return createPinger(scannerConfig.selectedPinger, scannerConfig.pingTimeout); + public Pinger createPinger(boolean isLAN) throws FetcherException { + Pinger mainPinger = createPinger(scannerConfig.selectedPinger, scannerConfig.pingTimeout); + if (isLAN) return new ARPPinger(injector.require(MACFetcher.class), mainPinger); + return mainPinger; } /** diff --git a/src/net/azib/ipscan/feeders/AbstractFeeder.java b/src/net/azib/ipscan/feeders/AbstractFeeder.java index 40b8e1b4b..e6e3b2831 100644 --- a/src/net/azib/ipscan/feeders/AbstractFeeder.java +++ b/src/net/azib/ipscan/feeders/AbstractFeeder.java @@ -7,6 +7,14 @@ package net.azib.ipscan.feeders; import net.azib.ipscan.config.Labels; +import net.azib.ipscan.core.ScanningSubject; + +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; + +import static net.azib.ipscan.util.InetAddressUtils.getInterface; +import static net.azib.ipscan.util.InetAddressUtils.matchingAddress; /** * Helper base class for built-in Feeders @@ -14,13 +22,27 @@ * @author Anton Keks */ public abstract class AbstractFeeder implements Feeder { - - public String getName() { + private NetworkInterface netIf; + private InterfaceAddress ifAddr; + + protected void initInterfaces(InetAddress ip) { + this.netIf = getInterface(ip); + this.ifAddr = matchingAddress(netIf, ip.getClass()); + } + + @Override public ScanningSubject subject(InetAddress ip) { + return new ScanningSubject(ip, netIf, ifAddr); + } + + @Override public String getName() { return Labels.getLabel(getId()); } - @Override - public String toString() { + @Override public boolean isLocalNetwork() { + return ifAddr != null; + } + + @Override public String toString() { return getName() + ": " + getInfo(); } } diff --git a/src/net/azib/ipscan/feeders/Feeder.java b/src/net/azib/ipscan/feeders/Feeder.java index 17bc5e94b..1fc6ff9cc 100644 --- a/src/net/azib/ipscan/feeders/Feeder.java +++ b/src/net/azib/ipscan/feeders/Feeder.java @@ -8,6 +8,8 @@ import net.azib.ipscan.core.Plugin; import net.azib.ipscan.core.ScanningSubject; +import java.net.InetAddress; + /** * Interface of a Feeder, which is used to feed scanner with IP addresses. * Basically, classes implementing Feeder must provide an algorithm of @@ -43,4 +45,15 @@ public interface Feeder extends Plugin { * Used for creation of Favorites, saving to file, etc. */ String getInfo(); + + /** + * @return true if scanning LAN addresses, so that ARP, etc can be used + */ + default boolean isLocalNetwork() { + return false; + } + + default ScanningSubject subject(InetAddress ip) { + return new ScanningSubject(ip); + } } diff --git a/src/net/azib/ipscan/feeders/RandomFeeder.java b/src/net/azib/ipscan/feeders/RandomFeeder.java index 633fe3a67..7e06be1a0 100644 --- a/src/net/azib/ipscan/feeders/RandomFeeder.java +++ b/src/net/azib/ipscan/feeders/RandomFeeder.java @@ -18,7 +18,6 @@ * @author Anton Keks */ public class RandomFeeder extends AbstractFeeder { - SecureRandom random = new SecureRandom(); InetAddress currentAddress; @@ -29,10 +28,7 @@ public class RandomFeeder extends AbstractFeeder { int addressCount; int currentNumber; - /** - * @see Feeder#getId() - */ - public String getId() { + @Override public String getId() { return "feeder.random"; } @@ -41,7 +37,9 @@ public RandomFeeder() { public RandomFeeder(String prototypeIP, String mask, int count) { try { - this.prototypeBytes = InetAddress.getByName(prototypeIP).getAddress(); + InetAddress ip = InetAddress.getByName(prototypeIP); + initInterfaces(ip); + this.prototypeBytes = ip.getAddress(); } catch (UnknownHostException e) { throw new FeederException("malformedIP"); diff --git a/src/net/azib/ipscan/feeders/RangeFeeder.java b/src/net/azib/ipscan/feeders/RangeFeeder.java index a10aa5891..5762985f8 100644 --- a/src/net/azib/ipscan/feeders/RangeFeeder.java +++ b/src/net/azib/ipscan/feeders/RangeFeeder.java @@ -10,11 +10,10 @@ import org.savarese.vserv.tcpip.OctetConverter; import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; import java.net.UnknownHostException; -import static net.azib.ipscan.util.InetAddressUtils.*; +import static net.azib.ipscan.util.InetAddressUtils.decrement; +import static net.azib.ipscan.util.InetAddressUtils.increment; /** * IP Range Feeder. @@ -24,8 +23,6 @@ * @author Anton Keks */ public class RangeFeeder extends AbstractFeeder { - private NetworkInterface netIf; - private InterfaceAddress ifAddr; private InetAddress startIP; private InetAddress endIP; private InetAddress originalEndIP; @@ -35,9 +32,6 @@ public class RangeFeeder extends AbstractFeeder { double percentageComplete; double percentageIncrement; - /** - * @see Feeder#getId() - */ public String getId() { return "feeder.range"; } @@ -49,8 +43,7 @@ public RangeFeeder(String startIP, String endIP) { try { this.startIP = this.currentIP = InetAddress.getByName(startIP); this.endIP = this.originalEndIP = InetAddress.getByName(endIP); - this.netIf = getInterface(this.startIP); - this.ifAddr = matchingAddress(netIf, this.startIP.getClass()); + initInterfaces(this.startIP); this.isReverse = false; } catch (UnknownHostException e) { @@ -66,7 +59,7 @@ public RangeFeeder(String startIP, String endIP) { initPercentageIncrement(); this.endIP = increment(this.endIP); } - + /** * Initalizes fields, used for computation of percentage of completion. */ @@ -95,7 +88,7 @@ private void initPercentageIncrement() { } else { this.currentIP = increment(prevIP); } - return new ScanningSubject(prevIP, netIf, ifAddr); + return subject(prevIP); } public int percentageComplete() { diff --git a/src/net/azib/ipscan/feeders/RescanFeeder.java b/src/net/azib/ipscan/feeders/RescanFeeder.java index bfd2a2b99..585dbcb0f 100644 --- a/src/net/azib/ipscan/feeders/RescanFeeder.java +++ b/src/net/azib/ipscan/feeders/RescanFeeder.java @@ -19,7 +19,6 @@ * @author Anton Keks */ public class RescanFeeder extends AbstractFeeder { - private Feeder originalFeeder; private List addresses; @@ -27,22 +26,20 @@ public class RescanFeeder extends AbstractFeeder { /** * Initializes the RescanFeeder using the old feeder used for the real scan to delegate some calls to. - * @param oldFeeder */ - public RescanFeeder(Feeder oldFeeder, String ... ips) { - this.originalFeeder = oldFeeder; + public RescanFeeder(Feeder originalFeeder, String ... ips) { + this.originalFeeder = originalFeeder; initAddresses(ips); } /** * @return the label of the "old" feeder */ - public String getId() { + @Override public String getId() { return originalFeeder.getId(); } - @Override - public String getName() { + @Override public String getName() { return Labels.getLabel("feeder.rescan.of") + originalFeeder.getName(); } @@ -66,22 +63,23 @@ private int initAddresses(String ... ips) { return ips.length; } - public boolean hasNext() { + @Override public boolean hasNext() { return current < addresses.size(); } - public ScanningSubject next() { - return new ScanningSubject(addresses.get(current++)); + @Override public ScanningSubject next() { + return originalFeeder.subject(addresses.get(current++)); } - public int percentageComplete() { + @Override public int percentageComplete() { return current * 100 / addresses.size(); } - /** - * @return the info of the "old" feeder - */ - public String getInfo() { + @Override public String getInfo() { return originalFeeder.getInfo(); } + + @Override public boolean isLocalNetwork() { + return originalFeeder.isLocalNetwork(); + } } diff --git a/src/net/azib/ipscan/fetchers/Fetcher.java b/src/net/azib/ipscan/fetchers/Fetcher.java index 2e4f29895..7d3fa91e2 100644 --- a/src/net/azib/ipscan/fetchers/Fetcher.java +++ b/src/net/azib/ipscan/fetchers/Fetcher.java @@ -8,6 +8,7 @@ import net.azib.ipscan.core.ScanningSubject; import net.azib.ipscan.core.values.NotAvailable; import net.azib.ipscan.core.values.NotScanned; +import net.azib.ipscan.feeders.Feeder; /** * Interface of all IP Fetchers. @@ -50,12 +51,16 @@ public interface Fetcher extends Cloneable, Plugin { * Special values may also be returned, such as {@link NotAvailable} or {@link NotScanned} */ Object scan(ScanningSubject subject); - + /** - * Called before scanning has started to do any intialization stuff + * Called before scanning has started to do any initialization stuff */ - void init(); - + default void init(Feeder feeder) { + init(); + } + + default void init() {} + /** * Called after the scanning has been completed to do any cleanup needed */ diff --git a/src/net/azib/ipscan/fetchers/PingFetcher.java b/src/net/azib/ipscan/fetchers/PingFetcher.java index 97436bae6..d2c391dbc 100644 --- a/src/net/azib/ipscan/fetchers/PingFetcher.java +++ b/src/net/azib/ipscan/fetchers/PingFetcher.java @@ -12,6 +12,7 @@ import net.azib.ipscan.core.net.Pinger; import net.azib.ipscan.core.net.PingerRegistry; import net.azib.ipscan.core.values.IntegerWithUnit; +import net.azib.ipscan.feeders.Feeder; import net.azib.ipscan.gui.fetchers.PingFetcherPrefs; import java.io.IOException; @@ -86,9 +87,9 @@ public Object scan(ScanningSubject subject) { return result.isAlive() ? new IntegerWithUnit(result.getAverageTime(), "ms") : null; } - public void init() { + public void init(Feeder feeder) { if (pinger == null) { - pinger = pingerRegistry.createPinger(); + pinger = pingerRegistry.createPinger(feeder.isLocalNetwork()); pingerUsers.set(1); } else diff --git a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java index 47f660df5..6e4612e55 100644 --- a/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java +++ b/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java @@ -141,9 +141,7 @@ private boolean preScanChecks() { MessageBox box = new MessageBox(resultTable.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.SHEET); box.setText(Labels.getLabel("text.scan.new")); box.setMessage(Labels.getLabel("text.scan.confirmation")); - if (box.open() != SWT.YES) { - return false; - } + return box.open() == SWT.YES; } return true; } diff --git a/test/net/azib/ipscan/core/ScannerTest.java b/test/net/azib/ipscan/core/ScannerTest.java index aca559f61..5d97104be 100644 --- a/test/net/azib/ipscan/core/ScannerTest.java +++ b/test/net/azib/ipscan/core/ScannerTest.java @@ -3,6 +3,7 @@ import net.azib.ipscan.core.ScanningResult.ResultType; import net.azib.ipscan.core.values.NotAvailable; import net.azib.ipscan.core.values.NotScanned; +import net.azib.ipscan.feeders.Feeder; import net.azib.ipscan.fetchers.AbstractFetcher; import net.azib.ipscan.fetchers.Fetcher; import net.azib.ipscan.fetchers.FetcherRegistry; @@ -80,8 +81,8 @@ public void testScanInterrupted() throws Exception { } @Test - public void testInit() throws Exception { - scanner.init(); + public void testInit() { + scanner.init(mock(Feeder.class)); assertTrue(initCalled.contains(FakeFetcher.class)); assertTrue(initCalled.contains(AnotherFakeFetcher.class)); @@ -89,7 +90,7 @@ public void testInit() throws Exception { } @Test - public void testCleanup() throws Exception { + public void testCleanup() { scanner.cleanup(); assertTrue(cleanupCalled.contains(FakeFetcher.class)); diff --git a/test/net/azib/ipscan/core/net/PingerRegistryTest.java b/test/net/azib/ipscan/core/net/PingerRegistryTest.java index 208e1afee..5dbc25151 100644 --- a/test/net/azib/ipscan/core/net/PingerRegistryTest.java +++ b/test/net/azib/ipscan/core/net/PingerRegistryTest.java @@ -48,7 +48,7 @@ public void createPinger() throws Exception { @Test public void createDefaultPinger() { config.selectedPinger = "pinger.udp"; - assertTrue(registry.createPinger() instanceof UDPPinger); + assertTrue(registry.createPinger(false) instanceof UDPPinger); } @Test diff --git a/test/net/azib/ipscan/feeders/RescanFeederTest.java b/test/net/azib/ipscan/feeders/RescanFeederTest.java index a7a56f0c8..7ee4dfa7e 100644 --- a/test/net/azib/ipscan/feeders/RescanFeederTest.java +++ b/test/net/azib/ipscan/feeders/RescanFeederTest.java @@ -6,13 +6,13 @@ package net.azib.ipscan.feeders; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - import net.azib.ipscan.config.Labels; - +import net.azib.ipscan.core.ScanningSubject; import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + /** * RescanFeederTest * @@ -36,7 +36,7 @@ public void testDelegatedMethods() { } @Test - public void testFunctionality() throws Exception { + public void addresses() { feeder = new RescanFeeder(mockFeeder(), "127.0.0.15", "127.0.1.35", "127.0.2.2"); assertTrue(feeder.hasNext()); @@ -60,6 +60,7 @@ private Feeder mockFeeder() { when(feeder.getInfo()).thenReturn("SomeInfo"); when(feeder.getId()).thenReturn("someLabel"); when(feeder.getName()).thenReturn("someName"); + when(feeder.subject(any())).thenAnswer(i -> new ScanningSubject(i.getArgument(0))); return feeder; }