Самый быстрый способ сканирования портов с помощью 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 минут?
Ответы
Ответ 1
Если вам нужен 200 мс для каждого из портов 65536 (в худшем случае брандмауэр блокирует все, что заставляет вас ударить ваш тайм-аут для каждого отдельного порта), математика довольно проста: вам нужно 13 тыс. секунд или около 3 часа с половиной.
У вас есть 2 (неисключительных) варианта, чтобы сделать это быстрее:
- уменьшить время ожидания
- paralellize ваш код
Поскольку операция связана с вводом-выводом (в отличие от привязки к ЦП, т.е. вы тратите время, ожидая ввода-вывода, а не для завершения какого-либо огромного вычисления), вы можете использовать многие, многие темы. Попробуйте начать с 20. Они разделили бы 3 часа с половиной среди них, , поэтому максимальное ожидаемое время составляет около 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
EDIT: На самом деле, я просто заметил: в этом конкретном случае вам не нужен класс ScanResult для хранения порта результата + и все еще известно, какой порт открыт. Поскольку вы добавляете фьючерсы в Список, который упорядочен, а затем вы обрабатываете их в том же порядке, в котором вы добавили их, у вас может быть счетчик, который вы увеличивали бы на каждой итерации, чтобы узнать, с каким портом вы имеете дело. Но, эй, это просто быть полным и точным. Никогда не пытайтесь делать это, это ужасно, мне в основном стыдно, что я думал об этом... Использование объекта ScanResult намного чище, код проще читать и обслуживать, а также позволяет, например, использовать CompletionService
для улучшения сканера.
Ответ 2
Помимо распараллеливания сканирования, вы можете использовать более совершенные методы сканирования портов, такие как те, которые сканируются (TCP SYN и TCP FIN): http://nmap.org/nmap_doc.html. Код реализации VB можно найти здесь: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html
Однако для использования этих методов вам необходимо использовать необработанные сокеты TCP/IP. Для этого вам следует использовать RockSaw.
Ответ 3
Пример кода вдохновлен "Бруно Рейсом"
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;
}
}
}
Ответ 4
Для Android попробуйте эту Awesome Library
https://github.com/stealthcopter/AndroidNetworkTools
Ответ 5
Если вы решили использовать опцию Nmap и хотите продолжить работу с Java, вам следует взглянуть на Nmap4j на SourceForge.net.
Это простой API, который позволяет интегрировать Nmap в Java-приложение.
Ответ 6
Я написал свой собственный асинхронный Java-сервис portscanner, который может сканировать порты через TCP-SYN-Scan, как это делает Nmap. Он также поддерживает сканирование ping IMCP и может работать с очень высокой пропускной способностью (в зависимости от того, что может поддерживать сеть):
https://github.com/subes/invesdwin-webproxy
Внутри он использует java binding pcap и предоставляет свои услуги через JMS/AMQP. Хотя вы также можете использовать его непосредственно в своем приложении, если не возражаете, если у вас есть права root.
Ответ 7
Нет, самый быстрый способ здесь - использовать метод динамически создаваемого потока
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)");
}
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;
}
}
}
Ответ 8
я не знаю, как использовать? simle yyyyyyyyyyyyyyyy