From 21a202062bdd9865d36316630f3000ffc9f647dd Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sun, 16 Dec 2012 17:17:56 +0200 Subject: [PATCH] make it possible to terminate socket operations in progress (this is possible by calling socket.close() from another thread) --- src/net/azib/ipscan/core/Scanner.java | 13 ++++- .../ipscan/core/ScannerDispatcherThread.java | 14 ++++-- .../ipscan/fetchers/NetBIOSInfoFetcher.java | 16 +++---- src/net/azib/ipscan/fetchers/PingFetcher.java | 36 +++++++------- .../azib/ipscan/fetchers/PingTTLFetcher.java | 4 +- .../azib/ipscan/fetchers/PortsFetcher.java | 47 ++++++++----------- src/net/azib/ipscan/util/IOUtils.java | 13 ++++- 7 files changed, 77 insertions(+), 66 deletions(-) diff --git a/src/net/azib/ipscan/core/Scanner.java b/src/net/azib/ipscan/core/Scanner.java index 17656aa6..08c265a3 100755 --- a/src/net/azib/ipscan/core/Scanner.java +++ b/src/net/azib/ipscan/core/Scanner.java @@ -10,6 +10,9 @@ import net.azib.ipscan.core.values.NotScanned; import net.azib.ipscan.fetchers.Fetcher; import net.azib.ipscan.fetchers.FetcherRegistry; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * Scanner functionality is encapsulated in this class. * It uses a list of fetchers to perform the actual scanning. @@ -17,8 +20,8 @@ import net.azib.ipscan.fetchers.FetcherRegistry; * @author Anton Keks */ public class Scanner { - private FetcherRegistry fetcherRegistry; + private Map currentFetchers = new ConcurrentHashMap(); public Scanner(FetcherRegistry fetcherRegistry) { this.fetcherRegistry = fetcherRegistry; @@ -34,6 +37,7 @@ public class Scanner { int fetcherIndex = 0; boolean isScanningInterrupted = false; for (Fetcher fetcher : fetcherRegistry.getSelectedFetchers()) { + currentFetchers.put(Thread.currentThread().getId(), fetcher); Object value = NotScanned.VALUE; if (!subject.isAddressAborted() && !isScanningInterrupted) { // run the fetcher @@ -50,6 +54,11 @@ public class Scanner { result.setType(subject.getResultType()); } + + public void interrupt(Thread thread) { + Fetcher fetcher = currentFetchers.get(thread.getId()); + if (fetcher != null) fetcher.cleanup(); + } /** * Init everything needed for scanning, including Fetchers @@ -64,9 +73,9 @@ public class Scanner { * Cleanup after a scan */ public void cleanup() { + currentFetchers.clear(); for (Fetcher fetcher : fetcherRegistry.getSelectedFetchers()) { fetcher.cleanup(); } } - } diff --git a/src/net/azib/ipscan/core/ScannerDispatcherThread.java b/src/net/azib/ipscan/core/ScannerDispatcherThread.java index 50ab376c..bd3297c3 100755 --- a/src/net/azib/ipscan/core/ScannerDispatcherThread.java +++ b/src/net/azib/ipscan/core/ScannerDispatcherThread.java @@ -158,11 +158,15 @@ public class ScannerDispatcherThread extends Thread implements ThreadFactory, St */ public Thread newThread(Runnable r) { // create IP threads in the specified group - Thread thread = new Thread(threadGroup, r); - // IP threads must be daemons, not preventing the JVM to terminate - thread.setDaemon(true); - - return thread; + return new Thread(threadGroup, r) { + // IP threads must be daemons, not preventing the JVM to terminate + { setDaemon(true); } + @Override + public void interrupt() { + scanner.interrupt(this); + super.interrupt(); + } + }; } /** diff --git a/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java b/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java index 736917e4..d4477ca2 100644 --- a/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java +++ b/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java @@ -6,16 +6,18 @@ package net.azib.ipscan.fetchers; +import net.azib.ipscan.config.LoggerFactory; +import net.azib.ipscan.config.ScannerConfig; +import net.azib.ipscan.core.ScanningSubject; + import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.net.SocketTimeoutException; -import java.util.logging.Level; import java.util.logging.Logger; -import net.azib.ipscan.config.ScannerConfig; -import net.azib.ipscan.config.LoggerFactory; -import net.azib.ipscan.core.ScanningSubject; +import static java.util.logging.Level.*; +import static net.azib.ipscan.util.IOUtils.*; /** * NetBIOSInfoFetcher - gathers NetBIOS info about Windows machines. @@ -84,13 +86,11 @@ public class NetBIOSInfoFetcher extends AbstractFetcher { } catch (Exception e) { // bugs? - LOG.log(Level.WARNING, null, e); + LOG.log(WARNING, null, e); return null; } finally { - if (socket != null) { - socket.close(); - } + closeQuietly(socket); } } diff --git a/src/net/azib/ipscan/fetchers/PingFetcher.java b/src/net/azib/ipscan/fetchers/PingFetcher.java index edec4802..f1e522d7 100755 --- a/src/net/azib/ipscan/fetchers/PingFetcher.java +++ b/src/net/azib/ipscan/fetchers/PingFetcher.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import static net.azib.ipscan.core.ScanningSubject.*; + /** * PingFetcher is able to ping IP addresses. * It returns the average round trip time of all pings sent. @@ -53,25 +55,21 @@ public class PingFetcher extends AbstractFetcher { } protected PingResult executePing(ScanningSubject subject) { - - PingResult result = null; - - if (subject.hasParameter(ScanningSubject.PARAMETER_PING_RESULT)) { - result = (PingResult) subject.getParameter(ScanningSubject.PARAMETER_PING_RESULT); - } - else { - try { - result = pinger.ping(subject, config.pingCount); - } - catch (IOException e) { - // if this is not a timeout - LOG.log(Level.WARNING, "Pinging failed", e); - // return an empty ping result - result = new PingResult(subject.getAddress()); - } - // remember the result for other fetchers to use - subject.setParameter(ScanningSubject.PARAMETER_PING_RESULT, result); - } + if (subject.hasParameter(PARAMETER_PING_RESULT)) + return (PingResult) subject.getParameter(PARAMETER_PING_RESULT); + + PingResult result; + try { + result = pinger.ping(subject, config.pingCount); + } + catch (IOException e) { + // if this is not a timeout + LOG.log(Level.WARNING, "Pinging failed", e); + // return an empty ping result + result = new PingResult(subject.getAddress()); + } + // remember the result for other fetchers to use + subject.setParameter(PARAMETER_PING_RESULT, result); return result; } diff --git a/src/net/azib/ipscan/fetchers/PingTTLFetcher.java b/src/net/azib/ipscan/fetchers/PingTTLFetcher.java index 0dd20ee1..1a71d559 100755 --- a/src/net/azib/ipscan/fetchers/PingTTLFetcher.java +++ b/src/net/azib/ipscan/fetchers/PingTTLFetcher.java @@ -5,8 +5,8 @@ package net.azib.ipscan.fetchers; import net.azib.ipscan.config.ScannerConfig; -import net.azib.ipscan.core.ScanningSubject; import net.azib.ipscan.core.ScanningResult.ResultType; +import net.azib.ipscan.core.ScanningSubject; import net.azib.ipscan.core.net.PingResult; import net.azib.ipscan.core.net.PingerRegistry; @@ -29,6 +29,6 @@ public class PingTTLFetcher extends PingFetcher { public Object scan(ScanningSubject subject) { PingResult result = executePing(subject); subject.setResultType(result.isAlive() ? ResultType.ALIVE : ResultType.DEAD); - return result.isAlive() && result.getTTL() > 0 ? new Integer(result.getTTL()) : null; + return result.isAlive() && result.getTTL() > 0 ? result.getTTL() : null; } } diff --git a/src/net/azib/ipscan/fetchers/PortsFetcher.java b/src/net/azib/ipscan/fetchers/PortsFetcher.java index 51742672..5e95c4be 100755 --- a/src/net/azib/ipscan/fetchers/PortsFetcher.java +++ b/src/net/azib/ipscan/fetchers/PortsFetcher.java @@ -5,6 +5,16 @@ */ package net.azib.ipscan.fetchers; +import net.azib.ipscan.config.ScannerConfig; +import net.azib.ipscan.core.PortIterator; +import net.azib.ipscan.core.ScanningResult.ResultType; +import net.azib.ipscan.core.ScanningSubject; +import net.azib.ipscan.core.values.NotScanned; +import net.azib.ipscan.core.values.NumericRangeList; +import net.azib.ipscan.gui.fetchers.PortsFetcherPrefs; +import net.azib.ipscan.util.SequenceIterator; +import net.azib.ipscan.util.ThreadResourceBinder; + import java.io.IOException; import java.net.ConnectException; import java.net.InetSocketAddress; @@ -14,15 +24,6 @@ import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; -import net.azib.ipscan.config.ScannerConfig; -import net.azib.ipscan.core.PortIterator; -import net.azib.ipscan.core.ScanningSubject; -import net.azib.ipscan.core.ScanningResult.ResultType; -import net.azib.ipscan.core.values.NotScanned; -import net.azib.ipscan.core.values.NumericRangeList; -import net.azib.ipscan.gui.fetchers.PortsFetcherPrefs; -import net.azib.ipscan.util.SequenceIterator; - /** * PortsFetcher scans TCP ports. * Port list is obtained using the {@link net.azib.ipscan.core.PortIterator}. @@ -37,6 +38,7 @@ public class PortsFetcher extends AbstractFetcher { private static final String PARAMETER_FILTERED_PORTS = "filteredPorts"; private ScannerConfig config; + private ThreadResourceBinder sockets = new ThreadResourceBinder(); // initialize preferences for this scan private PortIterator portIteratorPrototype; @@ -80,7 +82,6 @@ public class PortsFetcher extends AbstractFetcher { int portTimeout = subject.getAdaptedPortTimeout(); - Socket socket = null; // clone port iterator for performance instead of creating for every thread Iterator portsIterator = portIteratorPrototype.copy(); if (config.useRequestedPorts && subject.isAnyPortRequested()) { @@ -91,11 +92,10 @@ public class PortsFetcher extends AbstractFetcher { // no ports are configured for scanning return false; } - + while (portsIterator.hasNext() && !Thread.currentThread().isInterrupted()) { // TODO: UDP ports? - // TODO: reuse sockets? - socket = new Socket(); + Socket socket = sockets.bind(new Socket()); int port = portsIterator.next(); try { // set some optimization options @@ -108,9 +108,7 @@ public class PortsFetcher extends AbstractFetcher { socket.setSendBufferSize(16); socket.setTcpNoDelay(true); - if (socket.isConnected()) { - openPorts.add(port); - } + if (socket.isConnected()) openPorts.add(port); } catch (SocketTimeoutException e) { filteredPorts.add(port); @@ -120,29 +118,18 @@ public class PortsFetcher extends AbstractFetcher { assert e instanceof ConnectException : e; } finally { - try { - socket.close(); - } - catch (IOException e) {} + sockets.closeAndUnbind(socket); } } } return true; } - /** - * @param subject - * @return - */ @SuppressWarnings("unchecked") protected SortedSet getFilteredPorts(ScanningSubject subject) { return (SortedSet) subject.getParameter(PARAMETER_FILTERED_PORTS); } - /** - * @param subject - * @return - */ @SuppressWarnings("unchecked") protected SortedSet getOpenPorts(ScanningSubject subject) { return (SortedSet) subject.getParameter(PARAMETER_OPEN_PORTS); @@ -169,4 +156,8 @@ public class PortsFetcher extends AbstractFetcher { this.portIteratorPrototype = new PortIterator(config.portString); } + @Override + public void cleanup() { + sockets.close(); + } } diff --git a/src/net/azib/ipscan/util/IOUtils.java b/src/net/azib/ipscan/util/IOUtils.java index d94601bf..c36afd7e 100644 --- a/src/net/azib/ipscan/util/IOUtils.java +++ b/src/net/azib/ipscan/util/IOUtils.java @@ -1,13 +1,14 @@ package net.azib.ipscan.util; +import java.io.Closeable; import java.io.IOException; import java.net.DatagramSocket; import java.net.Socket; public class IOUtils { public static void closeQuietly(Socket socket) { - try { - if (socket != null) socket.close(); + if (socket != null) try { + socket.close(); } catch (IOException ignore) { } @@ -16,4 +17,12 @@ public class IOUtils { public static void closeQuietly(DatagramSocket socket) { if (socket != null) socket.close(); } + + public static void closeQuietly(Closeable closeable) { + if (closeable != null) try { + closeable.close(); + } + catch (IOException ignore) { + } + } }