Ruby listens IPv6

Please forgive me if this sounds a bit juvenile. I’m not as well
versed in network programming as I might like to be. Nevertheless,
here goes.

I’m looking at the book “Teach Yourself Ruby in 21 days” from Sams
publishing. I was reading “day 19” in which the Mr. Slagell presents a
page of code that implements a very simplistic web server. Since I’ve
done a little socket programming, I was curious to see what would
happen if I played with the code. I entered the following program :

···

#----------
require ‘socket’

def main
listener = TCPServer.new(“localhost”, (ARGV[0] || 80).to_i)
loop do
puts "listening"
session = listener.accept
session.each do | line |
puts line
end
session.close
end
end

main
#----------

I ran this code on my machine (as a privileged user so I could listen
to port 80) and happily tried to point my web browser at it to see what
kind of stuff the browser would send to the script.

Unfortunately, my web browser gave me a “could not connect” error.
Hrmmmm…

After trying the “server” on several ports with no success, checking my
firewall settings etc… and generally poking around I finally had the
wherewithall to run the program and use the “netstat” command to see if
the port was actually being listened to.

I found the line… it looks like this:

tcp6 0 0 localhost.http .
LISTEN

The interesting part is that this is the ONLY port on my whole system
that is marked as using IPv6. Hmmm…

What I know about IPv6 could probably fill a couple of sentences. They
go something like this, “IPv6 is the proposed standard for future
Internet communications. Among a host of features interesting to
networking type, one of the things it does is expands the pool of
available addresses that are available to computers in the world”.

My question is, can someone tell me why my Ruby script is listening on
IPv6? Is it because of the way that Ruby was compiled (I built 1.8 from
sources and am using that)? Or, do I have to do something in
particular to tell the Ruby socket that I want it to use IPv4?

I would also welcome pointers to documentation for neophytes about Ruby
sockets… though you may want to send them to me off-line so the other
folks on the list don’t have to endure them again. :slight_smile:

Scott

I think it was probably not “could not connect” exactly, more like “failed
to get any response from server”, since your program does not send back any
response down the TCP socket. Your web browser won’t display anything unless
the server follows the HTTP protocol.

I suggest you test your program using ‘telnet’. For example, modify it to
use port 8888 instead of port 80 (that way you don’t need to be root to run
it). Run it in one window, and in another do

   telnet 127.0.0.1 8888

This will establish a TCP connection; stuff you type in the telnet session
is sent down the connection, and anything which is sent back will be
displayed.

Your program works; Ruby will use an IPv6 socket if it’s available, but such
a socket is able to accept incoming IPv4 connections too.

To make such a TCP server work properly you really want to start off a new
thread for each incoming request, so that multiple requests can be handled
in parallel. There’s an example midway through

of a fake POP3 server.

But for a real-life webserver, you could just use Webrick - which is bundled
with ruby-1.8.0 now anyway.

Cheers,

Brian.

···

On Thu, Aug 14, 2003 at 07:08:42AM +0900, Scott Thompson wrote:

I ran this code on my machine (as a privileged user so I could listen
to port 80) and happily tried to point my web browser at it to see what
kind of stuff the browser would send to the script.

Unfortunately, my web browser gave me a “could not connect” error.
Hrmmmm…

Unfortunately, my web browser gave me a “could not connect” error.
Hrmmmm…

Out of curiosity, which web browser?

No doubt when ruby saw “localhost” and decided to go look it up. If in
/etc/hosts, ::1 is listed before 127.0.0.1, then it’ll bind to an IPv6
socket. If the IPv4 address is first, it’ll bind to the IPv4 address.

(Its not a good idea to switch this order though)

After trying the “server” on several ports with no success, checking my
firewall settings etc… and generally poking around I finally had the
wherewithall to run the program and use the “netstat” command to see if
the port was actually being listened to.

I found the line… it looks like this:

tcp6 0 0 localhost.http .
LISTEN

The interesting part is that this is the ONLY port on my whole system
that is marked as using IPv6. Hmmm…

That’s probably because the other services are not IPv6 enabled/aware.

What I know about IPv6 could probably fill a couple of sentences. They
go something like this, “IPv6 is the proposed standard for future
Internet communications. Among a host of features interesting to
networking type, one of the things it does is expands the pool of
available addresses that are available to computers in the world”.

Well, you can scratch ‘proposed’, but other than that, accurate.

My question is, can someone tell me why my Ruby script is listening on
IPv6? Is it because of the way that Ruby was compiled (I built 1.8 from
sources and am using that)? Or, do I have to do something in
particular to tell the Ruby socket that I want it to use IPv4?

If you want it to use IPv4, use an IPv4 address, “127.0.0.1” will do,
you can also get a browser that is IPv6 aware, and it’ll just work.
(Note that everybody with a valid IPv4 address has an IPv6 address
automatically.)

I would also welcome pointers to documentation for neophytes about Ruby
sockets… though you may want to send them to me off-line so the other
folks on the list don’t have to endure them again. :slight_smile:

The easiest way to do use sockets is threading:

server = TCPServer.open(host, port)

loop do
Thread.start(server.accept) do |s| # one thread per client

port = s.peeraddr[1]
name = s.peeraddr[2]
addr = s.peeraddr[3]

puts "*** recieving from #{name}:#{port}"

begin
  puts line while line = s.gets
ensure
  s.close
end

puts "*** done with #{name}:#{port}"

end
end

Each client connection starts a new thread to work with. You don’t
have to worry about select() because Ruby takes care of it for you
in the background.

And feel free to ask questions, that’s what we’re here for.

···

Scott Thompson (easco@mac.com) wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

I think it was probably not “could not connect” exactly, more like
“failed
to get any response from server”, since your program does not send
back any
response down the TCP socket. Your web browser won’t display anything
unless
the server follows the HTTP protocol.

Nope… It was a “could not connect”. I managed to get it to connect
to the ruby program by running ipconfig -a on my computer and
determining that my IPv6 address for the loopback connection is [::1].
By entering http:[::1]/ into my browser I was able to see the “stuff”
that I was interested in. Now the browser sat waiting for a response
until the connection timed out… but it was able to finally connect.

I was also able to get the ruby program to use IPv4 by replacing the
host name “localhost” in my ruby script with the IPv4 address
“127.0.0.1”.

That would imply to me that when Ruby resolves the hostname “localhost”
it probably gets back an IPv6 address and, therefore, chooses to create
an IPv6 socket.

Your program works; Ruby will use an IPv6 socket if it’s available,
but such
a socket is able to accept incoming IPv4 connections too.

Hmmm… for some reason that does not appear to be the case (at least
on my system). The IPv6 port is open and listening, but connecting to
it from the browser without specifying an IPv6 address fails.

I have a vague niggling at the base of my skull which tells me that
there may be a “problem” in the fact that I have a machine with two
network cards (a wireless card and a “hardline” ethernet card). I seem
to recall a post at one point to the effect that IPv6 to IPv4 may get
“confused” in this configuration because it’s not sure on which
interface it should translate IPv4 to IPv6… or something like that.
That may be total nonsense so no flames please.

To make such a TCP server work properly you really want to start off a
new
thread for each incoming request, so that multiple requests can be
handled
in parallel. There’s an example midway through
http://www.rubygarden.org/ruby?SingletonTutorial
of a fake POP3 server.

But for a real-life webserver, you could just use Webrick - which is
bundled
with ruby-1.8.0 now anyway.

Oh… well the goal was not to create a Web Server… rather to learn a
bit more about sockets. Thanks for the pointers though! I’ll look at
them.

Scott