Ticket #11 (new defect)

Opened 5 years ago

Last modified 4 years ago

Network.listenOn (PortNumber n) Sometimes Picks IPv6

Reported by: tibbe Owned by: bos
Priority: major Milestone:
Component: network Version:
Keywords: Cc: pho@…, cjs@…


When I run the following program on a NetBSD system, it listens on *.4444 for IPv6, not IPv4. This could certainly happen on other operating systems as well, though I've not tested others. Whether it happens or not could even vary from release to release.

import IO
import Network

main = do
    s <- listenOn (PortNumber 4444)
    (h, hostName, portNum) <- Network.accept s
    putStrLn $ "Connection from " ++ hostName ++ ":" ++ show portNum
    hPutStrLn h "Bye!"
    hClose h

I've verified that telnet 4444 produces a "connection refused" message, but telnet ::1 4444 connects and displays "Bye!"

I'd go so far to say that this is a bug. At best, one could argue that this behaviour is not entirely intuitive.

I propose that we (re-?)define PortID's PortNumber constructor to refer only to IPv4 protocol ports, and add a new PortNumber6 constructor for IPv6 ports.

Another option would be to do force PortNumber always to bind to IPv4, but not add PortNumber6. I prefer this less because we should be encouraging the use of IPv6, and because having PortNumber6 serves as obvious documentation for what PortNumber and PortNumber6 do.

A third option would be to attempt to listen on both IPv4 and IPv6, when both are available, but this sort of thing is fraught with many problems, the details of which are much more than I'd want to get into in this bug report.

I saw exactly the same problem on FreeBSD.

I propose that we (re-?)define PortID's PortNumber constructor to refer only to IPv4 protocol ports, and add a new PortNumber6 constructor for IPv6 ports.

That sounds highly reasonable. I prefer that way.

This sounds like a peculiarity of the BSD networking stack. On Linux, the code correctly chooses a wildcard address that responds to both IPv4 and IPv6 traffic.

I'd strongly suggest that a BSD user dig into the platform-specific problem. Suggesting an API change without any investigation of the underlying issue is not the right approach.

Actually, the linux code incorrectly binds to both; this opens up a security problem, though I can't remember the exact details right now. This is exactly the reason for the default sysctl setting of "net.inet6.ip6.v6only = 1", IIRC.

Here is a document discussing the security issues with IPv4-mapped addresses:


I'd say we definitely don't want to have to force people to enable this just to use Network, which is in the end supposed to make life simpler for basic networking.

The Linux behaviour does not appear to me to be incorrect, and the document to which you refer is concerned with threats due to the use of IPv4-mapped addresses on the wire.

The code in the Network module uses the Network.Socket code for its implementation. There are currently several knobs for controlling getAddrInfo that it does not use. Perhaps if you read up on a few of them and try them out, you might find a behaviour that you could then propose as more desirable.

Change History

Changed 5 years ago by tibbe

  • milestone set to

Changed 4 years ago by someone

Well, there are basically 2 possibilities: Either redefine the current behaviour to be v4-only and provide a new way for v6 (i.e. PortNumber6) or define the current behaviour to be "listen on everything possible" (by requesting a v6 socket and setsockopt'ing IPV6_V6ONLY to 0).

The former is probably less work and allows informed users more control, while the latter does the right thing automatically for users: There's rarely a good reason for being ip-version-dependent, and listening on v4 and not on v6 or vice versa opens you up for a nasty surprise when some other process grabs the other part. However, there might be problems with platforms not supporting IPV6_V6ONLY or dualstack sockets.

Note: See TracTickets for help on using tickets.