Commit b586b3bf authored by Ali Saeed's avatar Ali Saeed

Java Port Scanner

parents
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="JavaPortScanner" />
</profile>
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="60805bfa-ec04-44de-90b9-2a922cfe155f" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="JUnit3 Test Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="2Y5hR848ZiItt2hor8kbtSt0x2e" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary": "JUnit3",
"com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit3": "junit.framework.TestCase",
"last_opened_file_path": "C:/Users/dell/IdeaProjects/JavaPortScanner"
}
}]]></component>
<component name="RecentsManager">
<key name="CreateTestDialog.Recents.Supers">
<recent name="junit.framework.TestCase" />
</key>
<key name="CreateTestDialog.RecentsKey">
<recent name="org.example.scanner" />
<recent name="org.example.driver" />
</key>
</component>
<component name="RunManager" selected="JUnit.PortScannerTest.testIsPortOpen">
<configuration name="ParserTest.testInvalidInput" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="JavaPortScanner" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.example.driver.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.example.driver" />
<option name="MAIN_CLASS_NAME" value="org.example.driver.ParserTest" />
<option name="METHOD_NAME" value="testInvalidInput" />
<option name="TEST_OBJECT" value="method" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="ParserTest.testInvalidInputPort" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="JavaPortScanner" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.example.driver.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.example.driver" />
<option name="MAIN_CLASS_NAME" value="org.example.driver.ParserTest" />
<option name="METHOD_NAME" value="testInvalidInputPort" />
<option name="TEST_OBJECT" value="method" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="ParserTest.testRangeInput" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="JavaPortScanner" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.example.driver.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.example.driver" />
<option name="MAIN_CLASS_NAME" value="org.example.driver.ParserTest" />
<option name="METHOD_NAME" value="testRangeInput" />
<option name="TEST_OBJECT" value="method" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="ParserTest.testValidInput" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="JavaPortScanner" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.example.driver.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.example.driver" />
<option name="MAIN_CLASS_NAME" value="org.example.driver.ParserTest" />
<option name="METHOD_NAME" value="testValidInput" />
<option name="TEST_OBJECT" value="method" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="PortScannerTest.testIsPortOpen" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="JavaPortScanner" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.example.scanner.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.example.scanner" />
<option name="MAIN_CLASS_NAME" value="org.example.scanner.PortScannerTest" />
<option name="METHOD_NAME" value="testIsPortOpen" />
<option name="TEST_OBJECT" value="method" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="JUnit.PortScannerTest.testIsPortOpen" />
<item itemvalue="JUnit.ParserTest.testInvalidInputPort" />
<item itemvalue="JUnit.ParserTest.testRangeInput" />
<item itemvalue="JUnit.ParserTest.testInvalidInput" />
<item itemvalue="JUnit.ParserTest.testValidInput" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="60805bfa-ec04-44de-90b9-2a922cfe155f" name="Changes" comment="" />
<created>1699821338409</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1699821338409</updated>
</task>
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>JavaPortScanner</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
\ No newline at end of file
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
\ No newline at end of file
package org.example.driver;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.example.scanner.PortScanner;
import org.example.scanner.PortScannerResult;
import org.example.scanner.PortStatus;
/**
* USAGE: JavaPortScanner [target IP] [port range]
* <ul>
* <li>[target IP]: IP address to scan</li>
* <li>[port range]: range of ports to scan</li>
* <ul>
* <li>e.g. 80-100 scans from 80 to 100</li>
* <li>e.g. 8080 scans only port 8080</li>
* </ul>
* </ul>
*/
public class Driver {
private static final int SCAN_TIMEOUT = 200;
private static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors();
public static void main(String[] args) {
// get a list of Targets from Parser
List<Target> targets = null;
Scanner scanner = new Scanner(System.in);
System.out.println("Enter <IPv4> or <IPv4 Range (startIP-endIP)>");
String ipRange = scanner.nextLine();
System.out.println("Enter [PortStart-PortEnd], if you want to scan all ports enter \"all\" word, else if you want to scan one port just enter port number ");
String portRange = scanner.nextLine();
scanner.close();
ArrayList<String> arguments = new ArrayList<String>();
arguments.add(ipRange);
if (!portRange.equals("all")) {
arguments.add(portRange);
}
try {
targets = Parser.run(arguments);
} catch (InvalidUserInputException e) {
// exit with the message
System.err.println(e.getMessage());
System.exit(1);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
// lists to populate
List<List<Future<PortStatus>>> futureList = new ArrayList<List<Future<PortStatus>>>();
List<List<PortStatus>> resultList = new ArrayList<List<PortStatus>>();
// ExecutorService
// loop through the targets
for(int i=0; i<targets.size();i++){
ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT);
List<Future<PortStatus>> targetFutureList = new ArrayList<>();
for (int currPort = targets.get(i).portRange[0]; currPort <= targets.get(i).portRange[1]; currPort++) {
targetFutureList.add(PortScanner.isPortOpen(es, targets.get(i).targetIP, currPort, SCAN_TIMEOUT));
}
futureList.add(targetFutureList);
es.shutdown();
try {
if (!es.awaitTermination(2, TimeUnit.MINUTES)) {
// timeout elapsed before termination
es.shutdownNow();
}
} catch (InterruptedException e) {
es.shutdownNow();
}
}
for(int i=0; i<futureList.size();i++){
List<PortStatus> targetResultList = new ArrayList<>();
// get results to resultList
for (Future<PortStatus> eachFuture : futureList.get(i)) {
try {
targetResultList.add(eachFuture.get(2, TimeUnit.MINUTES));
} catch (Exception e) {
System.err.println("Timeout reached");
System.exit(1);
}
}
resultList.add(targetResultList);
}
// display results
List<PortScannerResult> portScannerResults = new ArrayList<>();
for(int i=0; i<resultList.size();i++ ){
PortScannerResult r = new PortScannerResult(resultList.get(i), resultList.get(i).get(0).getTargetIp());
portScannerResults.add(r);
}
for(int i=0; i<portScannerResults.size();i++){
System.out.println(portScannerResults.get(i));
}
}
}
package org.example.driver;
public final class InvalidUserInputException extends Exception {
/**
* IDE-generated serialVersionUID to suppress the warning.
*/
private static final long serialVersionUID = -3383457683260998220L;
/**
* Indicates which user-provided fields are invalid. Possible options are
* "ip", "port", or "both".
*/
private String invalidInputField;
/**
* Constructor for InvalidUserInputException. Parameter 'field' must be
* either "ip", "port", or "default" to indicate which inputs are invalid.
*
* @param field indicate which user-inputs are invalid
*/
public InvalidUserInputException(String field) {
if ((field.equals("ip") || field.equals("port")
|| field.equals("both"))) {
this.invalidInputField = field;
} else {
// set to "default" when field is non-acceptable
this.invalidInputField = "default";
}
}
@Override
public String getMessage() {
// generic default message
String ret = "Invalid user input";
if (!this.invalidInputField.equals("default")) {
// more specific message
ret += ": " + this.invalidInputField;
}
return ret;
}
}
package org.example.driver;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public final class Parser {
/**
* Get target information from user inputs.
*
* @param args arguments from user
*/
public static List<Target> run(ArrayList<String> args) throws InvalidUserInputException, UnknownHostException {
List<Target> targets = new ArrayList<>();
if (args.size() != 2 && args.size() != 1) {
throw new InvalidUserInputException("default");
}
// set ip range
String targetIPStart;
String targetIPEnd;
if (isValidIPv4Address(args.get(0))) {
// single IP address provided
targetIPStart = args.get(0);
targetIPEnd = args.get(0);
} else if (args.get(0).contains("-")) {
// range of IP addresses
// first occurrence of dash
int index = args.get(0).indexOf("-");
targetIPStart = args.get(0).substring(0, index);
targetIPEnd = args.get(0).substring(index + 1, args.get(0).length());
} else {
// invalid IP address
throw new InvalidUserInputException("ip");
}
long startIP = ipToLong(targetIPStart);
long endIP = ipToLong(targetIPEnd);
if (startIP > endIP) {
throw new InvalidUserInputException("ip");
}
// set port range
String targetPortStart;
String targetPortEnd;
if (args.size() == 1) {
// target all possible port if provided IP addresses are valid
targetPortStart = "1";
targetPortEnd = "65535";
} else if (args.get(1).contains("-")) {
// is a range
// first occurrence of dash
int index = args.get(1).indexOf("-");
targetPortStart = args.get(1).substring(0, index);
targetPortEnd = args.get(1).substring(index + 1, args.get(1).length());
} else {
// a single port
targetPortStart = args.get(1);
targetPortEnd = args.get(1);
}
int startPort;
int endPort;
try {
startPort = Integer.parseInt(targetPortStart);
endPort = Integer.parseInt(targetPortEnd);
// validate range
if (startPort > endPort || !isValidPortNumber(startPort) || !isValidPortNumber(endPort)) {
throw new InvalidUserInputException("port");
}
} catch (Exception e) {
// could not convert port num to int
throw new InvalidUserInputException("port");
}
// generate the list of targets
for (long ip = startIP; ip <= endIP; ip++) {
Target target = new Target();
target.targetIP = longToIp(ip);
target.portRange[0] = startPort;
target.portRange[1] = endPort;
targets.add(target);
}
return targets;
}
/**
* Return true iff portNum is in valid range, 1 to 65535.
*
* @param portNum port number to check.
* @return whether portNum is a valid port number.
*/
private static boolean isValidPortNumber(int portNum) {
return (1 <= portNum && portNum <= 65535);
}
/**
* Validate provided IPv4 address.
*
* @param ip
* @return whether ip is a valid IPv4 Address
*/
private static boolean isValidIPv4Address(String ip) {
try {
return InetAddress.getByName(ip).getHostAddress().equals(ip);
} catch (UnknownHostException e) {
return false;
}
}
private static long ipToLong(String ip) throws UnknownHostException {
InetAddress address = InetAddress.getByName(ip);
byte[] bytes = address.getAddress();
long result = 0;
for (byte b : bytes) {
result <<= 8;
result |= b & 0xFF;
}
return result;
}
private static String longToIp(long ip) {
StringBuilder sb = new StringBuilder(15);
for (int i = 0; i < 4; i++) {
sb.insert(0, Long.toString(ip & 0xff));
if (i < 3) {
sb.insert(0, '.');
}
ip >>= 8;
}
return sb.toString();
}
}
package org.example.driver;
public final class Target {
public String targetIP;
public int[] portRange;
public Target() {
portRange = new int[2];
}
}
\ No newline at end of file
package org.example.scanner;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public final class PortScanner {
public static Future<PortStatus> isPortOpen(ExecutorService es, String ip, int port,
int timeout) {
return es.submit(new Callable<PortStatus>() {
@Override
public PortStatus call() {
boolean stat;
try (Socket sfd = new Socket()) {
sfd.connect(new InetSocketAddress(ip, port), timeout);
// connection ok
// r.getOpenList().add(port);
stat = true;
} catch (Exception e) {
// connection failed
// r.getCloseList().add(port);
stat = false;
}
return new PortStatus(port, stat,ip);
}
});
}
}
package org.example.scanner;
import java.util.ArrayList;
import java.util.List;
public final class PortScannerResult {
private String targetIP;
private List<PortStatus> openList;
private List<PortStatus> closedList;
public String getTargetIP() {
return targetIP;
}
public void setTargetIP(String targetIP) {
this.targetIP = targetIP;
}
public PortScannerResult(List<PortStatus> resultList, String ip) {
this.targetIP =ip;
this.openList = new ArrayList<>();
this.closedList = new ArrayList<>();
for (PortStatus eachPortStatus : resultList) {
if (eachPortStatus.getIsOpen()) {
openList.add(eachPortStatus);
} else {
closedList.add(eachPortStatus);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
int totalCount = openList.size() + closedList.size();
sb.append(openList.size() + "/" + totalCount + " were open\n");
for (PortStatus openPortStatus : openList) {
sb.append(openPortStatus + "\n");
}
return "Target ip is : "+this.targetIP+"\n"+sb.toString();
}
}
package org.example.scanner;
public final class PortStatus {
private String targetIp;
private int portNum;
private boolean isOpen;
public PortStatus(int portNum, boolean isOpen, String targetIp) {
this.portNum = portNum;
this.isOpen = isOpen;
this.targetIp = targetIp;
}
public String getTargetIp() {
return targetIp;
}
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
public int getPortNum() {
return this.portNum;
}
public boolean getIsOpen() {
return this.isOpen;
}
@Override
public String toString() {
String status = "closed";
if (isOpen) {
status = "open";
}
return portNum + ":\t" + status;
}
public void setPortNum(int portNum) {
this.portNum = portNum;
}
}
package org.example.driver;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
public class ParserTest extends TestCase {
@Test
public void testValidInput() {
ArrayList<String> args = new ArrayList<>();
args.add("192.168.0.1");
args.add("8080");
try {
List<Target> targets = null;
try {
targets = Parser.run(args);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
Assert.assertEquals(1, targets.size());
Target target = targets.get(0);
Assert.assertEquals("192.168.0.1", target.targetIP);
Assert.assertArrayEquals(new int[]{8080, 8080}, target.portRange);
} catch (InvalidUserInputException e) {
Assert.fail("Unexpected exception occurred: " + e.getMessage());
}
}
@Test
public void testInvalidInputIP() {
ArrayList<String> args = new ArrayList<>();
args.add("15.15.12");
args.add("600");
try {
Parser.run(args);
Assert.fail("Expected InvalidUserInputException was not thrown.");
} catch (InvalidUserInputException e) {
Assert.assertEquals("Invalid user input: ip", e.getMessage());
} catch (UnknownHostException e) {
Assert.fail("Unexpected UnknownHostException occurred: " + e.getMessage());
}
}
@Test
public void testInvalidInputPort() {
ArrayList<String> args = new ArrayList<>();
args.add("192.168.12.1");
args.add("600000");
try {
Parser.run(args);
Assert.fail("Expected InvalidUserInputException was not thrown.");
} catch (InvalidUserInputException e) {
Assert.assertEquals("Invalid user input: port", e.getMessage());
} catch (UnknownHostException e) {
Assert.fail("Unexpected UnknownHostException occurred: " + e.getMessage());
}
}
@Test
public void testRangeInput() {
ArrayList<String> args = new ArrayList<>();
args.add("192.168.0.1-192.168.0.5");
args.add("8080-8090");
try {
List<Target> targets = Parser.run(args);
Assert.assertEquals(5, targets.size());
Target firstTarget = targets.get(0);
Target lastTarget = targets.get(4);
Assert.assertEquals("192.168.0.1", firstTarget.targetIP);
Assert.assertEquals("192.168.0.5", lastTarget.targetIP);
Assert.assertArrayEquals(new int[]{8080, 8090}, firstTarget.portRange);
Assert.assertArrayEquals(new int[]{8080, 8090}, lastTarget.portRange);
} catch (InvalidUserInputException | UnknownHostException e) {
Assert.fail("Unexpected exception occurred: " + e.getMessage());
}
}
}
\ No newline at end of file
package org.example.scanner;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class PortScannerTest extends TestCase {
@Test
public void testIsPortOpen() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
String ip = "127.0.0.1";
int port = 25;
int timeout = 2000;
Future<PortStatus> future = PortScanner.isPortOpen(executorService, ip, port, timeout);
PortStatus portStatus = future.get();
Assert.assertEquals(port, portStatus.getPortNum());
Assert.assertEquals(ip, portStatus.getTargetIp());
Assert.assertTrue(portStatus.getIsOpen());
executorService.shutdown();
}
}
\ No newline at end of file
File added
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment