Теперь пришел момент создать многопоточный сканер портов на Java. Это будет простая консольная утилита, которая в качестве параметров будет принимать имя или IP-адрес хоста, а также диапазон портов, которые нужно просканировать. Если диапазон не задан, то сканируются все порты от 0 до 65535.
Утилита состоит всего лишь из двух классов на Java. Рассмотрим первый класс -
PortScanWorker. Этот класс является "рабочей лошадкой", в которой и осуществляется сканирование портов. Вот его код:
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.io.IOException;
public class PortScanWorker implements Runnable {
static int globalId = 1;
private int id;
private List<Integer> ports;
private List<Integer> openPorts;
private InetAddress inetAddress;
private int timeout = 200;
CyclicBarrier barrier;
public PortScanWorker() {
id = globalId++;
}
public int getId() {
return id;
}
public void setBarrier(CyclicBarrier barrier) {
this.barrier = barrier;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public List<Integer> getOpenPorts() {
return openPorts;
}
public void setInetAddress(InetAddress inetAddress) {
this.inetAddress = inetAddress;
}
public void setPorts(List<Integer> ports) {
this.ports = ports;
}
public void run() {
//System.out.println("Started thread with id = " + id);
scan(inetAddress);
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
void scan(InetAddress inetAddress) {
openPorts = new ArrayList<Integer>();
//System.out.println("scanning ports: ");
for (Integer port : ports) {
//System.out.print(port);
try {
InetSocketAddress isa = new InetSocketAddress(inetAddress,port);
Socket socket = new Socket();
socket.connect(isa,timeout);
System.out.println("Found opened port: " + port);
openPorts.add(port);
socket.close();
} catch (IOException ioe) {
//System.out.println("");
}
}
//System.out.println("FINISH, id = " + id);
}
}
Перед началом сканирования нужно задать несколько параметров. Первый из параметров - IP-адрес хоста, задается методом
setInetAddress. Затем нужно задать список портов, которые будут сканироваться в данном потоке - это делается в методе
setPorts. Наконец, нужно задать
CyclicBarrier - это делается в методе
setBarrier.
В методе
scan происходит сканирование портов один за другим из списка
ports. Если порт открыт, то он добавляется в список
openPorts. После завершения работы метода
scan в методе
run происходит ожидание, пока остальные потоки не закончат работу. Ожидание происходит с помощью
CyclicBarrier.
Теперь рассмотрим второй класс -
Main:
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
public class Main {
public static final int MIN_PORTS_PER_THREAD = 20;
public static final int MAX_THREADS = 0xFF;
static InetAddress inetAddress;
static List<Integer> allPorts;
static List<Integer> allOpenPorts = new ArrayList<Integer>();
static List<PortScanWorker> workers = new ArrayList<PortScanWorker>(MAX_THREADS);
static Date startTime;
static Date endTime;
public static void main(String[] args) {
startTime = new Date();
processArgs(args);
if (allPorts.size()/MIN_PORTS_PER_THREAD > MAX_THREADS ) {
final int PORTS_PER_THREAD = allPorts.size()/MAX_THREADS;
List<Integer> threadPorts = new ArrayList<Integer>();
for (int i=0,counter=0; i<allPorts.size();i++,counter++) {
if (counter<PORTS_PER_THREAD) {
threadPorts.add(allPorts.get(i));
} else {
PortScanWorker psw = new PortScanWorker();
psw.setInetAddress(inetAddress);
psw.setPorts(new ArrayList<Integer>(threadPorts));
workers.add(psw);
threadPorts.clear();
counter=0;
}
}
PortScanWorker psw = new PortScanWorker();
psw.setInetAddress(inetAddress);
psw.setPorts(new ArrayList<Integer>(threadPorts));
workers.add(psw);
} else {
List<Integer> threadPorts = new ArrayList<Integer>();
for (int i=0,counter=0; i<allPorts.size();i++,counter++) {
if (counter<MIN_PORTS_PER_THREAD) {
threadPorts.add(allPorts.get(i));
} else {
PortScanWorker psw = new PortScanWorker();
psw.setInetAddress(inetAddress);
psw.setPorts(new ArrayList<Integer>(threadPorts));
workers.add(psw);
threadPorts.clear();
counter=0;
}
}
PortScanWorker psw = new PortScanWorker();
psw.setInetAddress(inetAddress);
psw.setPorts(new ArrayList<Integer>(threadPorts));
workers.add(psw);
}
System.out.println("Ports to scan: "+allPorts.size());
System.out.println("Threads to work: "+workers.size());
Runnable summarizer = new Runnable() {
public void run()
{
System.out.println("Scanning stopped...");
for (PortScanWorker psw : workers) {
List<Integer> openPorts = psw.getOpenPorts();
allOpenPorts.addAll(openPorts);
}
Collections.sort(allOpenPorts);
System.out.println("List of opened ports:");
for (Integer openedPort : allOpenPorts) {
System.out.println(openedPort);
}
endTime = new Date();
System.out.println("Time of run: " + (endTime.getTime() - startTime.getTime()) + " ms");
}
};
CyclicBarrier barrier = new CyclicBarrier(workers.size(),summarizer);
for (PortScanWorker psw : workers) {
psw.setBarrier(barrier);
}
System.out.println("Start scanning...");
for (PortScanWorker psw : workers) {
new Thread(psw).start();
}
}
static void processArgs(String[] args) {
if (args.length < 1) {
usage();
System.exit(1);
}
String host = args[0];
try {
inetAddress = InetAddress.getByName(host);
} catch (IOException ioe) {
System.out.println("Error when resolving host!");
System.exit(2);
}
System.out.println("Scanning host "+host);
int minPort = 0;
int maxPort = 0x10000-1;
if (args.length==2) {
if (args[1].indexOf("-")>-1) {
// range of ports pointed out
String[] ports = args[1].split("-");
try {
minPort = Integer.parseInt(ports[0]);
maxPort = Integer.parseInt(ports[1]);
} catch (NumberFormatException nfe) {
System.out.println("Wrong ports!");
System.exit(3);
}
} else {
// one port pointed out
try {
minPort = Integer.parseInt(args[1]);
maxPort = minPort;
} catch (NumberFormatException nfe) {
System.out.println("Wrong port!");
System.exit(3);
}
}
}
allPorts = new ArrayList<Integer>(maxPort-minPort+1);
for (int i=minPort; i<=maxPort; i++) {
allPorts.add(i);
}
}
static void usage() {
System.out.println("Java Port Scanner usage: ");
System.out.println("java Main host port");
System.out.println("Examples:");
System.out.println("java Main 192.168.1.1 1-1024");
System.out.println("java Main 192.168.1.1 1099");
System.out.println("java Main 192.168.1.1 (this scans all ports from 0 to 65535)");
}
}
Сначала в методе
main происходит разбор аргументов с помощью метода
processArgs. Также в методе
processArgs заполняется массив
allPorts, который содержит список всех портов, которые надо отсканировать.
Затем, после
processArgs идет создание (но не запуск!) объектов потоков (класс
PortScanWorker). Каждому объекту устанавливается хост и список портов, которые надо отсканить. Максимальное количество потоков, которое будет работать - 256 (параметр
MAX_THREADS). Минимальное количество портов на один поток - 20 (параметр
MIN_PORTS_PER_THREAD). В зависимости от того, какое ограничение будет выполнено, и создается список потоков. Все объекты-потоки сохраняются в список
workers. После того, как все создано, выводится статистика по общему количеству портов, которое надо отсканить и количеству потоков, которое будет это делать:
System.out.println("Ports to scan: "+allPorts.size());
System.out.println("Threads to work: "+workers.size());
Затем создается барьер:
CyclicBarrier barrier = new CyclicBarrier(workers.size(),summarizer);
которому задается общее количество потоков, которые должны просигналить барьеру, что они "добежали" до него, а также задается
Runnable, который выполнится после того, как будет получен сигнал от всех потоков:
Runnable summarizer = new Runnable() {
public void run()
{
System.out.println("Scanning stopped...");
for (PortScanWorker psw : workers) {
List<Integer> openPorts = psw.getOpenPorts();
allOpenPorts.addAll(openPorts);
}
Collections.sort(allOpenPorts);
System.out.println("List of opened ports:");
for (Integer openedPort : allOpenPorts) {
System.out.println(openedPort);
}
endTime = new Date();
System.out.println("Time of run: " + (endTime.getTime() - startTime.getTime()) + " ms");
}
};
В этом
Runnable и происходит все завершение работы. Найденные открытые порты копируются в общий список
allOpenPorts, который затем сортируется и выводится на экран. Затем подсчитывается общее время работы, которое также выводится на экран. После этого программа завершает работу.
Но вернемся к созданному барьеру. После того, как барьер был создан, мы его устанавливаем для каждого
PortScanWorker. И теперь, после того, как барьер установлен, ничего не мешает нам запустить потоки в работу:
for (PortScanWorker psw : workers) {
new Thread(psw).start();
}
Вот пример вывода программы:
Scanning host 192.168.1.1
Ports to scan: 65536
Threads to work: 256
Start scanning...
Found opened port: 80
Found opened port: 1900
Scanning stopped...
List of opened ports:
80
1900
Time of run: 51479 ms