#2391 Network.listenOn (PortNumber n) Sometimes Picks IPv6

So this has been sitting around for a bit:
http://hackage.haskell.org/trac/ghc/ticket/2391
And I'm sort of hoping we can get it resolved soon so that a fixed
version is included in GHC 6.10.
My basic thoughts are:
1. Listening on both IPV4 and IPV6, if I recall correctly,
introduces all sorts of interesting problems, and basically, you
don't want to go there. Or am I wrong about that?
2. We should default to IPv4 if someone uses the current interface, as
that's what most people will be expecting, as that's the Linux (and
probably Windows) behvaiour.
3. Given this, we should add a separate "PortNumber6 Int" value
constructor that informs listenOn to use IPv6 instead of IPv4, so that
people can use IPv6 with the simple interface.
Thoughts?
cjs
--
Curt Sampson

On 2008-07-21, Curt Sampson
So this has been sitting around for a bit:
http://hackage.haskell.org/trac/ghc/ticket/2391
And I'm sort of hoping we can get it resolved soon so that a fixed version is included in GHC 6.10.
My basic thoughts are:
1. Listening on both IPV4 and IPV6, if I recall correctly, introduces all sorts of interesting problems, and basically, you don't want to go there. Or am I wrong about that?
I don't see why it would introduce any more problems than listening on multiple IPv4 addresses, which is routine. However, this isn't something that listenOn can easily do by itself. The difficulty is, of course, is that listening on multiple points means having to multiplex them and select between them. I had heard that some OSes attempt to make it easier to transparently run old IPv4 programs by interpreting binding to all IPv4 addresses to include binding to IPv6, (or even vice-versa!), making it easy for addresses you don't want bound (say, because you want a different daemon handling it) to be bound. Basically, my position is that listenOn is the wrong interface -- it should take the host addresses as well as the port. The ports are the same namespace -- it shouldn't matter (much) whether TCP is running over IPv4 or IPv6.
2. We should default to IPv4 if someone uses the current interface, as that's what most people will be expecting, as that's the Linux (and probably Windows) behvaiour.
For a friendly interface, I'd have guessed they'd want to bind to both. But again, that's not trivial to portably shoehorn into one listening socket. When IPv6 is enabled, it calls getaddrinfo(), which should return both IPv6 and IPv4 connection results. But it looks like only the first one is being used, when both are valid:
addrs <- getAddrInfo (Just hints) Nothing (Just serv) let addr = head addrs
We could get around this by returning a composite pseudo-socket, representing both, and modifying Network.accept to take this pseudo-socket and wait for connections on any of the real sockets. But it's a bit ugly. And these pseudo-sockets won't work with Network.Socket.accept. If we give it a new type, I'm not entirely sure it's that bad a thing though. -- Aaron Denney -><-

On 2008-07-21 10:09 +0000 (Mon), Aaron Denney wrote:
On 2008-07-21, Curt Sampson
wrote: 1. Listening on both IPV4 and IPV6, if I recall correctly, introduces all sorts of interesting problems, and basically, you don't want to go there. Or am I wrong about that?
I don't see why it would introduce any more problems than listening on multiple IPv4 addresses, which is routine.
If I recall correctly: If you're listening on interface '*', you just call accept() and block. If you want to listen on two separate sockets, you can't do this. You can listen on IPv4 and IPv6 addresses using one socket by using IPv4 to IPv6 translation, but that was what got interesting. I think that this was more or less what you were saying. Anyway, I'm quite happy with having listenOn listen on all interfaces that implement v4 and all interfaces that implement v6; it's just whether it's implementable in some reasonable way that I worry about. And you'll note, for example, we already distinguish between a PortID for listening to "one of v4 or v6, depending on OS" (PortNumber Int) and for listening on Unix domain sockets (UnixSocket String).
Basically, my position is that listenOn is the wrong interface -- it should take the host addresses as well as the port.
Well, perhaps the idea there was that if you wanted to start doing this, you should be using Network.Socket instead of Network, anyway.
The ports are the same namespace -- it shouldn't matter (much) whether TCP is running over IPv4 or IPv6.
If you bind by hostname (e.g., "localhost") they have separate namespaces. I suppose you could take only addresses, but then you need some way to construct them, and before you know it you're starting to get as complex as Network.Socket.
We could get around this by returning a composite pseudo-socket, representing both, and modifying Network.accept to take this pseudo-socket and wait for connections on any of the real sockets. But it's a bit ugly. And these pseudo-sockets won't work with Network.Socket.accept. If we give it a new type, I'm not entirely sure it's that bad a thing though.
That's not an unreasonable option. But it's a lot more work to implement.
I think at this point I'm preferring the simple thing: just go for
PortNumber Int and PortNumber6 Int data constructors, and if you want to
do better than that, use Network.Socket.
cjs
--
Curt Sampson

On 2008-07-21, Curt Sampson
On 2008-07-21 10:09 +0000 (Mon), Aaron Denney wrote:
Basically, my position is that listenOn is the wrong interface -- it should take the host addresses as well as the port.
Well, perhaps the idea there was that if you wanted to start doing this, you should be using Network.Socket instead of Network, anyway.
Sounds reasonable to me -- my prefered solution would be to deprecate listenOn. But I don't have code using it. OTOH, the stability is "provisional". I think there is room for an easier to use interface than the full Network.Socket, but I don't think listenOn is it. Perhaps more like a parameterizable skeleton server?
The ports are the same namespace -- it shouldn't matter (much) whether TCP is running over IPv4 or IPv6.
If you bind by hostname (e.g., "localhost") they have separate namespaces. I suppose you could take only addresses, but then you need some way to construct them, and before you know it you're starting to get as complex as Network.Socket.
The same port will of course be connected differently on different machines. I think we're quibling over the meaning of namespace here, but e.g. port 22 is for ssh on both IPv4 and IPv6.
I think at this point I'm preferring the simple thing: just go for PortNumber Int and PortNumber6 Int data constructors, and if you want to do better than that, use Network.Socket.
And do we also have both Service and Service6 constructors? The same exact issue arises. -- Aaron Denney -><-

On 2008-07-21 12:54 +0000 (Mon), Aaron Denney wrote:
On 2008-07-21, Curt Sampson
wrote: Well, perhaps the idea there was that if you wanted to start doing this, you should be using Network.Socket instead of Network, anyway.
Sounds reasonable to me -- my prefered solution would be to deprecate listenOn.... I think there is room for an easier to use interface than the full Network.Socket, but I don't think listenOn is it. Perhaps more like a parameterizable skeleton server?
Personally, I like it. I use it myself when I just need to cons up a quick connection for some TCP stuff that I'll replace later with a proper setup (though I imagine I'll replace that with a library--or I'll have just memorized the API--somwhere around my fifth networking project in Haskell). But what I really imagined that the Network library was used for was student projects that focused on implementing a protocol talking to or listening on a TCP socket, where you wanted to focus on actually doing the protocol rather than the details of binding, name lookup, and so on. For this, it's perfect. The Network library is the equivalent of the bits of IO that are in the Prelude (putStrLn, etc.).
The same port will of course be connected differently on different machines. I think we're quibling over the meaning of namespace here, but e.g. port 22 is for ssh on both IPv4 and IPv6.
Indeed, but consider this: when I run netcat as "nc localhost 4321", I won't be able to connect to the Haskell program listening on that port on my NetBSD machine. Same goes if I use a non-v6 aware telnet with a domain name from any other machine. This is not a theoretical situation; I've twice in the past two weeks had to use Network.Socket instead of Network for just this reason. And it would have been the same on FreeBSD, from the reports I've heard. The main thing is, there are just too many machines that don't do IPv6, or are on networks not configured for IPv6, where perhaps the remote has that link-local-type IPv6 address, but the connecting host doesn't.
I think at this point I'm preferring the simple thing: just go for PortNumber Int and PortNumber6 Int data constructors, and if you want to do better than that, use Network.Socket.
And do we also have both Service and Service6 constructors? The same exact issue arises.
Well, I'd always wondered what those do! :-)
Yes, I'd say the same for those.
cjs
--
Curt Sampson
participants (2)
-
Aaron Denney
-
Curt Sampson