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 math.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 forio.sockets
andio.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:
( 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.
well, this works but it's pretty slow. what is wrong here?
ReplyDeleteUSING: calendar concurrency.combinators concurrency.semaphores
continuations fry io io.encodings.binary io.sockets io.timeouts
kernel math.parser sequences shuffle ;
IN: jbq-scanner
: open-port? ( host port -- ? ) [ binary [ t ] with-client ] [ 2drop f ] recover ;
: peach ( seq quot #limit -- )
swap [ ] dip '[ _ _ with-semaphore ] parallel-each ;
: scanit ( host range -- )
[ tuck
20 milliseconds [ open-port? ] with-timeout*
[ number>string "port " " is opened." surround print ] [ number>string print ] if ] with 8 peach ;
! "127.0.0.1" 1024 [1,b] scanit
oops, there was a lt semaphore gt in the peach word, between "swap [" and "] dip".
ReplyDelete