2

I've been trying to create an application which needs to scan open ports on a network (mostly LAN) as fast as possible.

I searched around and one great method that I found uses the following code:

(1 to 65536).par.map { case port ⇒
  try {
    val socket = new java.net.Socket("127.0.0.1", port)
    socket.close()
    println(port)
    port
  } catch {
    case _: Throwable ⇒ -1
  }
}.toSet

However, the problem with the code is that if I enter anything other than 127.0.0.1 or localhost as location (say 192.168.1.2), the application freezes.

Any idea why this happens and how I can fix it?

P.S. I also tried setting socket timeout with socket.setSoTimeout(1500), but no change.

Chetan Bhasin
  • 3,503
  • 1
  • 23
  • 36
  • When you make a connection, you block waiting until the remote host either responds or the connection attempt times out. In your case, attempting to connect to a non-existent host means that you'll spend all your time waiting for timeouts. As with *any* program, you would have realized this if you didn't ignore exceptions. – kdgregory Jul 28 '14 at 18:37
  • @kdgregory I tried setting up socket.setSoTimeout(3000) but that didn't work either. – Chetan Bhasin Jul 29 '14 at 06:20
  • 3 seconds multiplied by 65536 is still a lot of time. – Ashalynd Jul 29 '14 at 09:11
  • http://stackoverflow.com/questions/434718/sockets-discover-port-availability-using-java – Ashalynd Jul 29 '14 at 09:19
  • @Ashalynd but considering that it's in parallel, it shouldn't matter much. Right? – Chetan Bhasin Jul 29 '14 at 09:59
  • 2
    You, and the people who upvoted you, need to read some documentation. "Parallel" does not mean "infinite threads." – kdgregory Jul 29 '14 at 10:36
  • 1
    People are asking questions to learn. Nothing wrong with that. – Ashalynd Jul 29 '14 at 11:25
  • there is another relevant question: http://stackoverflow.com/questions/11547082/fastest-way-to-scan-ports-with-java – Ashalynd Jul 29 '14 at 11:38
  • You are right in all senses. What I don't understand though is why doesn't the application hang when scanning local network. – Chetan Bhasin Aug 02 '14 at 07:15

2 Answers2

6

Something like

import scala.concurrent.{Future, Await}
import scala.concurrent.duration._
import scala.util.Try
import scala.concurrent._
import java.util.concurrent.Executors

implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(100)) 

def openPorts(address:String ="127.0.0.1",duration:Duration = 10 seconds, fromPort:Int = 1, toPort:Int = 65536) = {
 val socketTimeout = 200
 val result = Future.traverse(fromPort to toPort ) { port =>
  Future{ Try {
    val socket = new java.net.Socket()
    socket.connect(new java.net.InetSocketAddress(address, port),socketTimeout)
    socket.close()
    port
  } toOption } 
 } 
 Try {Await.result(result, duration)}.toOption.getOrElse(Nil).flatten
}


scala> val localPorts openPorts(fromPort = 10, toPort = 1000)
localPorts: scala.collection.immutable.IndexedSeq[Int] = Vector(22, 631)
scala> val remotePorts = openPorts(fromPort = 10, toPort = 1000, address="192.168.1.20")
remotePorts: scala.collection.immutable.Seq[Int] = List() //we ate the timeout

scala> val remotePorts = openPorts(fromPort = 12000, toPort = 13000, address="91.190.218.61", duration=30 seconds)
remotePorts: scala.collection.immutable.Seq[Int] = Vector(12345, 12350)
Ashalynd
  • 12,363
  • 2
  • 34
  • 37
2

Although Ashalynd's answer works well too, while I was playing with this application again I found a solution, perhaps simpler, using Futures.

Here is the code

  import scala.concurrent._
  import ExecutionContext.Implicits.global

  object TestOne extends App{

    println("Program is now running")

    val dataset: Future[Set[Int]] = future {
      (1 to 6335).map {
        case port =>
          try {
            val socket = new java.net.Socket("127.0.0.1", port)
            socket.close()
            println("The port is open at " + port)
            port
          } catch {
            case _: Throwable => -1
          }
      }.toSet
    }
  }
Chetan Bhasin
  • 3,503
  • 1
  • 23
  • 36
  • The difference with my solution is that you still go through the list of ports sequentially, whereas Future.traverse can do it in parallel (if execution context is configured accordingly, of course). – Ashalynd Oct 21 '14 at 18:23
  • 1
    Yes, you're right. Thanks for the answer. The time when I asked this question I was new to Scala and was more aware of concurrency model of scripting languages like JS. I've been playing with Scala Futures for some time and now I understand why your solution is better. – Chetan Bhasin Oct 22 '14 at 05:32