Skip to content

Instantly share code, notes, and snippets.

@picatz
Last active June 7, 2018 22:34
Show Gist options
  • Save picatz/e8a6f526ce236481837f3cdbb9b84152 to your computer and use it in GitHub Desktop.
Save picatz/e8a6f526ce236481837f3cdbb9b84152 to your computer and use it in GitHub Desktop.
asynchronous port scanner in ruby
require 'async/io'
require 'async/await'
require 'async/semaphore'
class PortScanner
include Async::Await
include Async::IO
def initialize(host: '127.0.0.1', ports:)
@host = host
@ports = ports
@semaphore = Async::Semaphore.new(`ulimit -n`.to_i)
end
def scan_port(port, timeout: 0.5)
timeout(timeout) do
Async::IO::Endpoint.tcp(@host, port).connect do |peer|
peer.close
puts "#{port} open"
end
end
rescue Errno::ECONNREFUSED, Async::TimeoutError
puts "#{port} closed"
rescue Errno::EMFILE
sleep timeout
retry
end
async def start(timeout: 0.5)
@ports.map do |port|
@semaphore.async do
scan_port(port, timeout: timeout)
end
end.collect(&:result)
end
end
scanner = PortScanner.new(ports: (1..1024))
scanner.start
@picatz
Copy link
Author

picatz commented Jun 7, 2018

@ioquatix Async::Semaphore seems to be a nice abstraction to help clean up the code and provide a similar semantic for people that understand Ruby's multi-threading APIs. 👌

However, I still seem to reach the too many files limit since, I'd assume, other operations on my system have files that are open. So, I still end up hitting Errno::EMFILE, or using it to keep track of the situation myself with raise Errno::EMFILE while @open_fds >= @fd_limit which I don't mind doing in this case -- I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment