Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

Архив блога

пятница, 17 сентября 2010 г.

Многопоточный сканер портов

Теперь пришел момент создать многопоточный сканер портов на 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

1 комментарий:

Постоянные читатели