Re: Factor

Factor: the language, the theory, and the practice.

Port Scanner

Tuesday, February 15, 2011

#networking

For fun, I thought I’d show how to build a simple port scanner using Factor

We start by listing vocabularies that we will be using and a namespace for our work:

USING: continuations formatting kernel ranges
io.encodings.binary io.sockets make sequences ;

IN: port-scan

We need to decide how to check for an open port. In this case, we are going to attempt to establish a TCP connection. If it succeeds, then the port is open, otherwise it will throw an error connecting and we will assume the port was not open.

: open-port? ( host port -- ? )
    <inet> [ binary [ t ] with-client ] [ 2drop f ] recover ;

Note: we should be setting a connection timeout, so that we do not let a connection attempt last forever. However, I’m not quite sure how to do that – the documentation for io.sockets and io.timeouts didn’t make it obvious.

Next, we will make a word that returns an array of all open ports (checking ports from 1 to 1024). We use the make vocabulary to build the sequence dynamically.

: open-ports ( host -- seq )
    1024 [1..b] [
        [ 2dup open-port? [ , ] [ drop ] if ] each drop
    ] { } make ;

Finally, we can make a word that provides some visual output back to the user:

: scan-ports ( host -- )
    [ "Scanning %s...\n" printf ]
    [ open-ports [ "%d is open\n" printf ] each ]
    bi ;

Using this on my laptop returns the following:

IN: scratchpad "127.0.0.1" scan-ports
Scanning 127.0.0.1...
631 is open

It is quite simple and functional as is. However, some obvious improvements could be made:

  • adding the connection timeout as mentioned above
  • providing the output of scan-ports to the user as open ports are found
  • using the concurrent combinators to test ports in parallel
  • using a list of port numbers to identify services that might be on open ports

The code for this is on my GitHub.