Самый быстрый способ сканирования портов с помощью 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;
    }

}
}

для Android попробуйте эту удивительную библиотеку

https://github.com/stealthcopter/AndroidNetworkTools


Если вы решили использовать опцию 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 разрешения.