aesiniath / http-streams

Haskell HTTP client library for use with io-streams
https://hackage.haskell.org/package/http-streams
BSD 3-Clause "New" or "Revised" License
50 stars 48 forks source link

openConnectionSSL can fail if the system supports ipv6 #33

Closed bsrkaditya closed 11 years ago

bsrkaditya commented 11 years ago

openConnectionSSL assumes that the socket's family is AF_INET.

Specifically it is failing for the case of google.

import qualified Data.ByteString.Char8 as BS import Network.Http.Client import OpenSSL (withOpenSSL)

main = withOpenSSL $ do ctx <- baselineContextSSL c <- openConnectionSSL ctx (BS.pack "www.google.com") 443 closeConnection c

istathar commented 11 years ago

Weird. Google is of course one of the test cases I used. Thanks for the patch; I'll have a test here and apply it, I'm sure.

AfC

istathar commented 11 years ago

So I tried 'master' here just now, and it's fine. The system I'm testing on has IPv6 support, and your "test case" above works fine. Can you elaborate further on how to demonstrate the problem?

AfC

bsrkaditya commented 11 years ago

openConnectionSSL uses getAddrInfo to resolve the address of the hostname. getAddrInfo returns a list of addresses, out of which the first one is picked. openConnectionSSL opens a socket of AF_INET family. If the first address happens to be also of AF_INET family, then it is going to work. If however the address happens to be of AF_INET6 family, then it will fail. This is what happened in my case.

So the test is not machine independent. :-) However I can confirm that it is indeed failing in my case. It gives an error: connect: unsupported operation (Address family not supported by protocol) Running it with strace, I got this line: connect(7, {sa_family=AF_INET6, sin6_port=htons(443), inet_pton(AF_INET6, "2404:6800:400a:801::1013", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 EAFNOSUPPORT (Address family not supported by protocol)

The sort order of getaddrinfo is specified here (section 6, destination address selection): http://www.ietf.org/rfc/rfc3484.txt

By my naive understanding, Rule 7 "Prefer native transport", and Section 5 Rule 5 "Prefer outgoing interface" could be the likely reasons for a difference in the ordering.

bsrkaditya commented 11 years ago

So running:

import Network.Socket import Control.Monad main = getAddrInfo Nothing (Just "www.google.com") (Just "443") >>= mapM_ (putStrLn.show)

I got

AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [2404:6800:400a:801::1010]:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET6, addrSocketType = Datagram, addrProtocol = 17, addrAddress = [2404:6800:400a:801::1010]:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET6, addrSocketType = Raw, addrProtocol = 0, addrAddress = [2404:6800:400a:801::1010]:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 74.125.235.242:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 74.125.235.242:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Raw, addrProtocol = 0, addrAddress = 74.125.235.242:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 74.125.235.244:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 74.125.235.244:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Raw, addrProtocol = 0, addrAddress = 74.125.235.244:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 74.125.235.243:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 74.125.235.243:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Raw, addrProtocol = 0, addrAddress = 74.125.235.243:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 74.125.235.240:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 74.125.235.240:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Raw, addrProtocol = 0, addrAddress = 74.125.235.240:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 74.125.235.241:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 74.125.235.241:443, addrCanonName = Nothing} AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET, addrSocketType = Raw, addrProtocol = 0, addrAddress = 74.125.235.241:443, addrCanonName = Nothing}

The address family of the first address is AF_INET6. :-)

istathar commented 11 years ago

@bsrkaditya fair enough; your patch makes sense. I'll double check it doesn't break here (given my otherwise working circumstances) but will apply.

AfC