非常に単純なポートスキャナーを作成しましたが、実行速度が遅すぎるため、より高速にスキャンする方法を探しています。ここに私のコードがあります:
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でスキャンするには時間がかかります。5分未満で0〜65535をスキャンできる他の方法はありますか?
65536個のポートごとに200ミリ秒が必要な場合(最悪の場合、ファイアウォールがすべてをブロックしているため、すべてのポートでタイムアウトになります)、計算は非常に簡単です:13k秒、または約3時間必要です半分。
より高速にするための2つの(非排他的)オプションがあります。
操作はI/Oバウンドであるため(CPUバウンドとは対照的に、つまり、巨大な計算が完了するのではなく、I/Oを待つことに時間を費やします)、多数のスレッドを使用できます。 20から始めてみてください。3時間半を分割しますしたがって、最大予想時間は約10分です。これは反対側に圧力をかけることを覚えておいてください。つまり、スキャンされたホストは「不合理な」または「奇妙な」パターンで巨大なネットワークアクティビティを見るため、スキャンを非常に簡単に検出できます。
最も簡単な方法(つまり、最小限の変更)は、ExecutorServiceおよびFuture APIを使用することです。
_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
に変更し、true
またはfalse
の代わりにnew ScanResult(port, true)
またはnew ScanResult(port, false)
を返します。
編集:実際、私はちょうど気づいた:この特定のケースでは、結果とポートを保持するためにScanResultクラスを必要とせず、まだ開いているポートを知っています。先物をList、つまりorderedに追加し、後で追加したのと同じ順序で処理、繰り返しごとにインクリメントするカウンターを使用して、処理しているポートを知ることができます。しかし、ちょっと、これは完全で正確であることです。 これをやろうとしないでください、それは恐ろしいです、私はこれについて考えたことをほとんど恥ずかしく思います... ScanResultオブジェクトを使用する方がはるかにきれいです、コードは読みやすく保守しやすい方法で、たとえば、CompletionService
を使用してスキャナーを改善できます。
スキャンの並列化とは別に、ここで説明されているもの(TCP SYNおよびTCP FINスキャン)などのより高度なポートスキャン技術を使用できます。 http://nmap.org/nmap_doc .html 。VB実装のコードはここにあります: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html
ただし、これらの手法を使用するには、生のTCP/IPソケットを使用する必要があります。これには RockSaw ライブラリを使用する必要があります。
コードサンプルは「ブルーノレイス」に触発されています
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 on SourceForge.net を参照してください。
これは、Nmap=をJavaアプリに統合できるシンプルなAPIです。
私は独自の非同期portscannerを作成しましたJava TCP-SYN-Scanを介してポートをスキャンできるサービスはNmapのようになります。IMCPpingスキャンもサポートします。非常に高いスループット(ネットワークが維持できるものに応じて):
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)");
}
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;
}
}
}