Самый быстрый способ сканирования портов с помощью Java
Я сделал очень простой сканер портов, но он работает слишком медленно, поэтому я ищу способ сделать его быстрее. Вот мой код:
public boolean portIsOpen(String ip, int port, int timeout) {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
этот код здесь проверяет, открыт ли определенный порт на определенном ip. Для таймаута я использую минимальное значение 200, потому что, когда я иду ниже, у него недостаточно времени для тестирования порта. Ну он хорошо работает, но занимает слишком много для сканирования от 0 до 65535. Есть ли другой способ, который может сканировать от 0 до 65535 менее чем за 5 минут?
6 ответов
Если вам нужно 200 мс для каждого из 65536 портов (в худшем случае брандмауэр блокирует все, что делает вас ударил тайм-аут для каждого порта), математика довольно проста: вам нужно 13k секунд, или около 3 часов с половиной.
у вас есть 2 (неисключительные) опции, чтобы сделать его быстрее:
- уменьшить свои ожидания
- paralellize код
так как операция связана с вводом-выводом (в отличие от CPU -- то есть вы тратите время на ожидание ввода-вывода, а не на какой-то огромный расчет для завершения), вы можете использовать много, много нитей. Попробуйте начать с 20. Они поделили бы между собой 3 с половиной часа,таким образом, максимальное ожидаемое время составляет около 10 минут. Просто помните, что это окажет давление на другую сторону, т. е. отсканированный хост увидит огромную сетевую активность с "необоснованными" или "странными" шаблонами, что делает сканирование чрезвычайно простым обнаруживать.
самый простой способ (т. е. с минимальными изменениями) использовать ExecutorService и будущие интерфейсы:
public static Future<Boolean> portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) {
return es.submit(new Callable<Boolean>() {
@Override public Boolean call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
});
}
затем вы можете сделать что-то вроде:
public static void main(final String... args) {
final ExecutorService es = Executors.newFixedThreadPool(20);
final String ip = "127.0.0.1";
final int timeout = 200;
final List<Future<Boolean>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.shutdown();
int openPorts = 0;
for (final Future<Boolean> f : futures) {
if (f.get()) {
openPorts++;
}
}
System.out.println("There are " + openPorts + " open ports on host " + ip + " (probed with a timeout of " + timeout + "ms)");
}
Если вам нужно знать какие порты открыты (а не просто сколько, как в приведенном выше примере), вам нужно будет изменить возвращаемый тип функции на Future<SomethingElse>
, где SomethingElse
будет держать порт и результат сканирования, что-то например:
public final class ScanResult {
private final int port;
private final boolean isOpen;
// constructor
// getters
}
затем измените Boolean
to ScanResult
в первом фрагменте и return new ScanResult(port, true)
или просто true
или false
EDIT: на самом деле, я только что заметил: в этом конкретном случае вам не нужен класс ScanResult для хранения result + port и все еще знаете, какой порт открыт. Так как вы добавляете фьючерсы в список, которая составляет приказал, а позже обработайте их в том же порядке, в котором вы добавили их!--24-->, у вас может быть счетчик, который вы увеличиваете на каждой итерации, чтобы знать, с каким портом вы имеете дело. Но, эй, это просто чтобы быть полным и точным. никогда не пытайтесь сделать это, это ужасно, мне в основном стыдно, что я думал об этом... использование объекта ScanResult намного чище, код легче читать и поддерживать, и позволяет, позже, например, использовать CompletionService
для улучшения сканера.
помимо распараллеливания сканирования, вы можете использовать более продвинутые методы сканирования портов, такие как (TCP SYN и TCP FIN scanning), описанные здесь:http://nmap.org/nmap_doc.html. VB код реализации можно найти здесь:http://h.ackack.net/spoon-worlds-fastest-port-scanner.html
однако для использования этих методов вам необходимо использовать необработанные сокеты TCP/IP. Вы должны использовать RockSaw библиотека для этого.
пример кода вдохновлен "Bruno Reis"
class PortScanner {
public static void main(final String... args) throws InterruptedException, ExecutionException {
final ExecutorService es = Executors.newFixedThreadPool(20);
final String ip = "127.0.0.1";
final int timeout = 200;
final List<Future<ScanResult>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
// for (int port = 1; port <= 80; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.awaitTermination(200L, TimeUnit.MILLISECONDS);
int openPorts = 0;
for (final Future<ScanResult> f : futures) {
if (f.get().isOpen()) {
openPorts++;
System.out.println(f.get().getPort());
}
}
System.out.println("There are " + openPorts + " open ports on host " + ip + " (probed with a timeout of "
+ timeout + "ms)");
}
public static Future<ScanResult> portIsOpen(final ExecutorService es, final String ip, final int port,
final int timeout) {
return es.submit(new Callable<ScanResult>() {
@Override
public ScanResult call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return new ScanResult(port, true);
} catch (Exception ex) {
return new ScanResult(port, false);
}
}
});
}
public static class ScanResult {
private int port;
private boolean isOpen;
public ScanResult(int port, boolean isOpen) {
super();
this.port = port;
this.isOpen = isOpen;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
}
}
Если вы решили использовать опцию Nmap и хотите продолжить работу с Java, вы должны посмотреть Nmap4j на SourceForge.net (http://nmap4j.sourceforge.net). Это простой API, который позволяет интегрировать Nmap в приложение java.
--Джон
Я написал свою собственную асинхронную службу Java portscanner, которая может сканировать порты через TCP-SYN-Scan, как это делает Nmap. Он также поддерживает сканирование imcp ping и может работать с очень высокой пропускной способностью (в зависимости от того, что может поддерживать сеть):
https://github.com/subes/invesdwin-webproxy
внутренне он использует Java-привязку pcap и предоставляет свои услуги через JMS/AMQP. Хотя вы также можете использовать его непосредственно в своем приложении, если вы не возражаете, что у него есть root разрешения.