Non-blocking Networking in Ruby
First, let me say that figuring out how to do Non-blocking IO in Ruby is a sparsely documented *pain in the ass.* Apparently non-blocking IO is only built into Ruby from version 1.8.4, and even then only through importing fcntl and fighting through making your socket object a non-blocking descriptor. New in 1.8.5 are non-blocking methods for common actions that will handle the descriptor handling for you, making things much nicer.
This all came about because I was writing a port knocking utility in Ruby because I got tired of studying for finals. If you want to see an example of a non-blocking socket connect, follow the code below. I’ll comment on it in a bit.
require 'socket'
include Socket::Constants
if $*.length [port 2] ... [port N] \n"
exit
end
for i in 1...$*.length
s = Socket.new( AF_INET, SOCK_STREAM, 0 )
sa = Socket.pack_sockaddr_in( $*[i], $*[0] )
begin
s.connect_nonblock( sa )
rescue Errno::EINPROGRESS
end
end
The first part of the code should be self-explanatory for anyone familiar with ruby. We just import the socket library, and include the constants from within so we don’t have to prepend Socket:: to all of our constants later. Following this immediately is an argument check - we want at least a hostname and one port to knock, but we can accept as many ports as the user can provide.
The second loop is the more interesting part. It starts off by creating a stream socket that expects a hostname or IP address as an endpoint. Then we create a socket address object containing the port ($*[i] = args[i]) and the hostname (contained in $*[0]). After the socket address has been created, we throw in the ‘begin’ statement so we can catch errors later on. Then comes the non-blocking connect call: the program execution will not wait for the socket to establish a connection before moving on, which is what we want when portknocking because no connection will be established when knocking. The rescue statement is so that we don’t get an error the next time the socket tries a connection. If we didn’t have it, ruby would complain that we already have a socket operation in progress and exit for us.
The script is simple, but this took a long time to figure out. I love ruby, but sometimes the documentation on niche areas of coding are a little weak. For other non-blocking IO procedures, look in the Ruby 1.8.5 core API: http://www.ruby-doc.org/stdlib