Самый быстрый способ сканирования портов с помощью 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 минут?
9 ответов
Если вам нужно 200 мс для каждого из 65536 портов (в худшем случае брандмауэр блокирует все, что приводит к превышению времени ожидания для каждого отдельного порта), математика довольно проста: вам нужно 13 тыс. Секунд или около 3 часов и половина.
У вас есть 2 (неисключительные) опции, чтобы сделать это быстрее:
- сократить время ожидания
- паралеллизировать ваш код
Поскольку операция связана с вводом / выводом (в отличие от привязки к процессору, т. Е. Вы тратите время на ожидание ввода / вывода, а не на выполнение каких-то огромных вычислений), вы можете использовать множество потоков. Попробуйте начать с 20. Они разделят между собой три с половиной часа, поэтому максимальное ожидаемое время составляет около 10 минут. Просто помните, что это окажет давление на другую сторону, т. Е. Сканируемый хост увидит огромную сетевую активность с "необоснованными" или "странными" шаблонами, что сделает сканирование чрезвычайно простым для обнаружения.
Самый простой способ (т.е. с минимальными изменениями) - это использовать API ExecutorService и Future:
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
в ScanResult
в первом фрагменте, и вернуться new ScanResult(port, true)
или же new ScanResult(port, false)
вместо просто true
или же false
РЕДАКТИРОВАТЬ: На самом деле, я только что заметил: в этом конкретном случае вам не нужен класс ScanResult для хранения результирующий + порт, и по-прежнему знать, какой порт открыт. Поскольку вы добавляете фьючерсы в упорядоченныйсписок, а затем обрабатываете их в том же порядке, в котором вы их добавили, у вас может быть счетчик, который вы будете увеличивать на каждой итерации, чтобы узнать, с каким портом вы работаете., Но, эй, это просто чтобы быть полным и точным. Никогда не пытайтесь делать это, это ужасно, мне, как правило, стыдно, что я об этом подумал... Использование объекта ScanResult намного чище, код намного легче читать и поддерживать, и позволяет вам позже например, используйте CompletionService
улучшить сканер.
Образец кода вдохновлен "Бруно Рейс"
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;
}
}
}
Помимо распараллеливания сканирования, вы можете использовать более продвинутые методы сканирования портов, подобные описанным здесь (сканирование TCP SYN и TCP FIN): http://nmap.org/nmap_doc.html. VB-код реализации можно найти здесь: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html
Однако, чтобы использовать эти методы, вам нужно использовать необработанные сокеты TCP/IP. Вы должны использовать библиотеку RockSaw для этого.
Если вы решили использовать опцию Nmap и хотите продолжить работу с Java, вам следует взглянуть на Nmap4j на SourceForge.net (http://nmap4j.sourceforge.net). Это простой API, который позволяет интегрировать Nmap в Java-приложение.
--Jon
Для Android попробуйте эту Awesome Library
Я написал свой собственный java-сервис асинхронного сканера портов, который может сканировать порты через TCP-SYN-Scan, как это делает Nmap. Он также поддерживает пинг-сканирование IMCP и может работать с очень высокой пропускной способностью (в зависимости от того, что может выдержать сеть):
https://github.com/subes/invesdwin-webproxy
Внутренне он использует java-привязку pcap и предоставляет свои сервисы через JMS/AMQP. Хотя вы также можете использовать его непосредственно в своем приложении, если не возражаете против наличия прав root.
Нет, самый быстрый способ - использовать динамически созданный метод потока
Executors.newCachedThreadPool();
Таким образом, он использует потоки до тех пор, пока все они не будут заняты, а затем, когда все они будут заняты и появится новая задача, он откроет новый поток и выполнит на нем новую задачу.
Вот мой фрагмент кода (Кредиты принадлежат Джеку и Бруно Рейсу)
Я также добавил функцию поиска любого IP-адреса, который вы вводите, для некоторых дополнительных функций и простоты использования.
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
class PortScanner {
public static void main(final String... args) throws InterruptedException, ExecutionException
{
final ExecutorService es = Executors.newCachedThreadPool();
System.out.print("Please input the ip address you would like to scan for open ports: ");
Scanner inputScanner = new Scanner(System.in);
final String ip = inputScanner.nextLine();
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)");
es.shutdown();
}
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;
}
}
}
Я могу опоздать с этим, но вы можете выполнить массовое сканирование портов, выполнив следующие действия, используя однопоточность NIO2. Следуя коду NIO2 с помощью одного потока, я могу сканировать все хосты на наличие заданного порта. Пожалуйста, попробуйте разумный тайм-аут и убедитесь, что у вас большой файловый дескриптор для процесса.
public static List<HostTarget> getRechabilityStatus(String...hosts,final int port, final int bulkDevicesPingTimeoutinMS) throws Exception {
List<AsynchronousSocketChannel> channels = new ArrayList<>(hosts.length);
try {
List<CompletableFuture<HostTarget>> all = new ArrayList<>(hosts.length);
List<HostTarget> allHosts = new ArrayList(hosts.length);
for (String host : hosts) {
InetSocketAddress address = new InetSocketAddress(host, port);
HostTarget target = new HostTarget();
target.setIpAddress(host);
allHosts.add(target);
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
channels.add(client);
final CompletableFuture<HostTarget> targetFuture = new CompletableFuture<>();
all.add(targetFuture);
client.connect(address, target, new CompletionHandler<Void, HostTarget>() {
@Override
public void completed(Void result, HostTarget attachment) {
attachment.setIsReachable(true);
targetFuture.complete(attachment);
}
@Override
public void failed(Throwable exc, HostTarget attachment) {
attachment.setIsReachable(false);
attachment.errrorMessage = exc.getMessage();
targetFuture.complete(attachment);
}
});
}
try {
if(bulkDevicesPingTimeoutinMS > 0) {
CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).get(bulkDevicesPingTimeoutinMS, TimeUnit.MILLISECONDS);
}else{
// wait for all future to be complete 1000 scan is taking 7 seconds.
CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).join();
}
} catch (Exception timeoutException) {
// ignore
}
return allHosts;
}finally {
for(AsynchronousSocketChannel channel : channels){
try{
channel.close();
}catch (Exception e){
if(LOGGER.isDebugEnabled()) {
LOGGER.error("Erorr while closing socket",e);
}
}
}
}
static class HostTarget {
String ipAddress;
Boolean isReachable;
public String getIpAddress() {
return ipAddress;
}
public Boolean getIsReachable() {
return isReachable;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public void setIsReachable(Boolean isReachable) {
this.isReachable = isReachable;
}
}
Все вдохновлено вами, но только этот код действительно сработал!
класс PortScaner
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class PortScaner {
public static void main(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++)
futures.add(portIsOpen(es, ip, port, timeout));
es.shutdown();
int openPorts = 0;
for (final Future<ScanResult> f : futures)
if (f.get().isOpen()) {
openPorts++;
System.out.println(f.get());
}
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);
}
}
});
}
}
класс ScanResult
public final class ScanResult {
private final int port;
private final boolean isOpen;
public ScanResult(int port, boolean isOpen) {
super();
this.port = port;
this.isOpen = isOpen;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @return the isOpen
*/
public boolean isOpen() {
return isOpen;
}
@Override
public String toString() {
return "ScanResult [port=" + port + ", isOpen=" + isOpen + "]";
}
}