11package com .codedead .advancedportchecker .domain .controller ;
22
33import android .content .Context ;
4- import android .os .AsyncTask ;
54
65import com .codedead .advancedportchecker .R ;
76import com .codedead .advancedportchecker .domain .object .ScanProgress ;
1110import java .net .InetSocketAddress ;
1211import java .net .Socket ;
1312import java .net .SocketTimeoutException ;
13+ import java .util .concurrent .ExecutorService ;
14+ import java .util .concurrent .Executors ;
15+ import java .util .concurrent .TimeUnit ;
1416
15- public final class ScanController extends AsyncTask <Void , ScanProgress , Void > {
16-
17- private final String host ;
18- private final int startPort ;
19- private final int endPort ;
20- private final int timeOut ;
17+ public final class ScanController {
2118
19+ private final Context context ;
20+ private ExecutorService executorService ;
21+ private final int poolSize ;
2222 private final AsyncResponse response ;
2323
2424 /**
2525 * Initialize a new ScanController
2626 *
27- * @param context The context that can be used to retrieve error messages
28- * @param host The host address that needs to be scanned
27+ * @param context The context that can be used to retrieve error messages
28+ * @param response The AsyncResponse that can be called when a scan has finished or been cancelled
29+ */
30+ public ScanController (final Context context ,
31+ final AsyncResponse response ) {
32+ if (context == null )
33+ throw new NullPointerException ("Context cannot be null!" );
34+
35+ this .context = context ;
36+
37+ final int numberOfCores = Runtime .getRuntime ().availableProcessors ();
38+ poolSize = Math .max (2 , Math .min (numberOfCores * 2 , 4 ));
39+
40+ this .response = response ;
41+ }
42+
43+ /**
44+ * Start scanning a host for open ports
45+ *
46+ * @param host The host that needs to be scanned
2947 * @param startPort The initial port for a range of ports that need to be scanned
3048 * @param endPort The final port in a range of ports that need to be scanned
3149 * @param timeOut The time it takes before a connection is marked as timed-out
32- * @param response The AsyncResponse that can be called when a scan has finished or been cancelled
3350 */
34- public ScanController (final Context context , String host , final int startPort ,
35- final int endPort , final int timeOut ,
36- final AsyncResponse response ) {
37-
38- if (context == null ) throw new NullPointerException ("Context cannot be null!" );
51+ public void startScan (String host , final int startPort , final int endPort , final int timeOut ) {
3952 if (host == null || host .isEmpty () || !UtilController .isValidAddress (host ))
4053 throw new IllegalArgumentException (context .getString (R .string .string_invalid_host ));
4154 if (response == null )
@@ -50,43 +63,59 @@ public ScanController(final Context context, String host, final int startPort,
5063 if (endPort > 65535 )
5164 throw new IllegalArgumentException (context .getString (R .string .string_largest_possible_port ));
5265
66+ final int totalNumberOfPorts = endPort - startPort + 1 ;
67+ // Divide the total number of ports by the number of cores
68+ final int numberOfPortsPerThread = totalNumberOfPorts / poolSize ;
69+ // Calculate the remaining ports that need to be scanned
70+ final int remainingPorts = totalNumberOfPorts % poolSize ;
71+
72+ executorService = Executors .newFixedThreadPool (Math .min (totalNumberOfPorts , poolSize ));
73+
5374 host = host
5475 .replace ("http://" , "" )
5576 .replace ("https://" , "" )
56- .replace ("ftp://" , "" );
77+ .replace ("ftp://" , "" )
78+ .replace ("ssh://" , "" )
79+ .replace ("telnet://" , "" )
80+ .replace ("smtp://" , "" );
5781
58- this .host = host ;
59- this .startPort = startPort ;
60- this .endPort = endPort ;
61- this .timeOut = timeOut ;
62- this .response = response ;
63- }
6482
65- @ Override
66- protected Void doInBackground (final Void ... voids ) {
67- int currentPort = startPort ;
68- while (currentPort <= endPort ) {
69- if (isCancelled ()) {
70- break ;
83+ final String finalHost = host ;
84+ for (int i = 0 ; i < poolSize ; i ++) {
85+ final int currentStartPort = startPort + i * numberOfPortsPerThread ;
86+ int currentEndPort = currentStartPort + numberOfPortsPerThread - 1 ;
87+
88+ if (i == poolSize - 1 ) {
89+ // Add the remaining ports to the last thread
90+ currentEndPort += remainingPorts ;
7191 }
72- publishProgress (scanTcp (host , currentPort , timeOut ));
73- currentPort ++;
74- }
75- return null ;
76- }
7792
78- @ Override
79- protected void onPostExecute (final Void aVoid ) {
80- response .scanComplete ();
81- super .onPostExecute (aVoid );
93+ int finalCurrentEndPort = currentEndPort ;
94+ executorService .execute (() -> {
95+ // Loop over the ports to scan
96+ for (int currentPort = currentStartPort ; currentPort <= finalCurrentEndPort ; currentPort ++) {
97+ if (Thread .currentThread ().isInterrupted ()) {
98+ break ;
99+ }
100+ final ScanProgress scan = scanTcp (finalHost , currentPort , timeOut );
101+ response .update (scan );
102+ }
103+ });
104+ }
82105 }
83106
84- @ Override
85- protected void onProgressUpdate (final ScanProgress ... values ) {
86- if (isCancelled ()) return ;
107+ /**
108+ * Cancel the current scan
109+ *
110+ * @throws InterruptedException If the cancellation cannot be awaited
111+ */
112+ public void cancelScan () throws InterruptedException {
113+ if (executorService == null )
114+ return ;
87115
88- response .update (values [0 ]);
89- super .onProgressUpdate (values );
116+ executorService .shutdownNow ();
117+ //noinspection ResultOfMethodCallIgnored
118+ executorService .awaitTermination (30000 , TimeUnit .MILLISECONDS );
90119 }
91120
92121 /**
@@ -112,10 +141,4 @@ private static ScanProgress scanTcp(final String host, final int port, final int
112141
113142 return scan ;
114143 }
115-
116- @ Override
117- protected void onCancelled () {
118- response .scanCancelled ();
119- super .onCancelled ();
120- }
121144}
0 commit comments