Fastest way to scan ports with Java

后端 未结 6 1358
借酒劲吻你
借酒劲吻你 2020-11-30 21:46

I made a very simple port scanner, but it runs too slow, so I\'m looking for a way to make it scan faster. Here is my code:

public boolean portIsOpen(String          


        
相关标签:
6条回答
  • 2020-11-30 21:54

    Nay, fastest way here is to use the dynamically created thread method

    Executors.newCachedThreadPool();
    

    This way it uses threads until all of them are taken, then when all of them are taken and there is a new task it will open up a new thread and preform the new task on it.

    Here's my code snippet (Creds due to Jack and Bruno Reis)

    I also added the function to search any IP address you type in for some added functionality and ease of use.

        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;
    }
    
        }
        }
    
    0 讨论(0)
  • 2020-11-30 21:56

    Apart from parallelizing the scan, you can use more advanced port scanning techniques like the ones (TCP SYN and TCP FIN scanning) explained here: http://nmap.org/nmap_doc.html. VB code of an implementation can be found here: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html

    In order to use these techniques, however, you need to use raw TCP/IP sockets. You should use RockSaw library for this.

    0 讨论(0)
  • 2020-11-30 21:57

    Code sample is inspired by "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;
        }
    
    }
    }
    
    0 讨论(0)
  • 2020-11-30 22:10

    I wrote my own asynchronous portscanner java service that can scan ports via TCP-SYN-Scan like Nmap does. It also support IMCP ping scans and can work with a very high throughput (depending on what the network can sustain):

    https://github.com/subes/invesdwin-webproxy

    Internally it uses a java binding pcap and exposes its services via JMS/AMQP. Though you can also use it directly in your application if you don't mind it having root permissions.

    0 讨论(0)
  • 2020-11-30 22:15

    If you decide to use the Nmap option and want to continue with Java, you should look at Nmap4j on SourceForge.net.

    It's a simple API that allows you to integrate Nmap into a java app.

    0 讨论(0)
  • 2020-11-30 22:19

    If you need 200ms for each of the 65536 ports (in the worst case, a firewall is blocking everything, thus making you hit your timeout for every single port), the maths is pretty simple: you need 13k seconds, or about 3 hours and a half.

    You have 2 (non-exclusive) options to make it faster:

    • reduce your timeout
    • paralellize your code

    Since the operation is I/O bound (in contrast to CPU bound -- that is, you spend time waiting for I/O, and not for some huge calculation to complete), you can use many, many threads. Try starting with 20. They would divide the 3 hours and a half among them, so the maximum expected time is about 10 minutes. Just remember that this will put pressure on the other side, ie, the scanned host will see huge network activity with "unreasonable" or "strange" patterns, making the scan extremely easy to detect.

    The easiest way (ie, with minimal changes) is to use the ExecutorService and Future APIs:

    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;
            }
          }
       });
    }
    

    Then, you can do something like:

    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)");
    }
    

    If you need to know which ports are open (and not just how many, as in the above example), you'd need to change the return type of the function to Future<SomethingElse>, where SomethingElse would hold the port and the result of the scan, something like:

    public final class ScanResult {
      private final int port;
      private final boolean isOpen;
      // constructor
      // getters
    }
    

    Then, change Boolean to ScanResult in the first snippet, and return new ScanResult(port, true) or new ScanResult(port, false) instead of just true or false

    EDIT: Actually, I just noticed: in this particular case, you don't need the ScanResult class to hold result + port, and still know which port is open. Since you add the futures to a List, which is ordered, and, later on, you process them in the same order you added them, you could have a counter that you'd increment on each iteration to know which port you are dealing with. But, hey, this is just to be complete and precise. Don't ever try doing that, it is horrible, I'm mostly ashamed that I thought about this... Using the ScanResult object is much cleaner, the code is way easier to read and maintain, and allows you to, later, for example, use a CompletionService to improve the scanner.

    0 讨论(0)
提交回复
热议问题