Reputation: 2565
I have this piece of code connecting to local graphite (which is actually just nc -l -p 2023
running on localhost):
getCarbonAddr :: Config -> IO SockAddr
getCarbonAddr cfg = do
let host = (graphiteHost . graphiteConfig) cfg
let port = (graphitePort . graphiteConfig) cfg
-- addrInfos <- getAddrInfo (Just defaultHints)
addrInfos <- getAddrInfo Nothing
(Just host)
(Just (show port))
putStrLn $ "addrInfos: " ++ show addrInfos
c <- case addrInfos of
(addrInfo : _) -> return (addrAddress addrInfo)
_ -> unsupportedAddressError host
return c
where
unsupportedAddressError h = ioError $ userError $
"unsupported address: " ++ h
Config values for host and port are "localhost" and 2023 respectively. When I run this on my OS X after upgrade to Yosemite -- I see the following crash:
addrInfos: [AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Datagram, addrProtocol = 17, addrAddress = [::1]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [::1]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 127.0.0.1:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 127.0.0.1:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Datagram, addrProtocol = 17, addrAddress = [fe80::1%lo0]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [fe80::1%lo0]:2023, addrCanonName = Nothing}]
LocalJob: connect: unsupported operation (Address family not supported by protocol family)
This seemed weird to me, so I decided to run this C program (googled for "getaddrinfo" example, changed hostname and port, added printing of ai_family):
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
int main(void)
{
struct addrinfo *result;
struct addrinfo *res;
int error;
/* resolve the domain name into a list of addresses */
error = getaddrinfo("localhost", "2023", NULL, &result);
if (error != 0)
{
fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error));
return EXIT_FAILURE;
}
/* loop over all returned results and do inverse lookup */
for (res = result; res != NULL; res = res->ai_next)
{
char hostname[NI_MAXHOST] = "";
error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0);
if (error != 0)
{
fprintf(stderr, "error in getnameinfo: %s\n", gai_strerror(error));
continue;
}
if (*hostname != '\0')
printf("hostname: %s. ai_family: %i\n", hostname, res->ai_family);
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
After launching it, I saw this output:
➜ getaddrinfotest ./main
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 2
hostname: localhost. ai_family: 2
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 30
So, the ai_family 30 seems to be a weird thing. As I understand from socket.h sources, it is AF_TIPC protocol, which is quite a rare thing I didn't heard of before. I also opened haskell's packFamily' sources and was surprise to see it doesn't handle value of 30 there (doesn't know about AF_TIPC).
The questions I have are: what's the best thing to do now? Did I get the problem correctly? Should haskell handle unknown ai family better? // thanks!
UPDATE: I resolved an issue by putting hint to use ipv4:
addrInfos <- getAddrInfo (Just (defaultHints { addrFamily=AF_INET }))
(Just host)
(Just (show port))
but I still wonder how to resolve this issue "the right way".
Upvotes: 3
Views: 1449
Reputation: 5651
By default, getaddrinfo
(and its Haskell binding getAddrInfo
) returns a linked-list of addrinfo that can contain IPv4 addresses, IPv6 addresses, or a mixture of the two. Unfortunately, the sockets functions do not allow connecting an IPv4 socket to an IPv6 address, so as you iterate through the list of addresses, you need to create a socket of the right type:
addrinfos <- getAddrInfo Nothing (Just hostname) (Just (show port))
let first = head addrinfos
sock <- socket (addrFamily first) Stream defaultProtocol
connect sock (addrAddress first)
Note how (addrFamily first)
is passed to the socket
function, so that the socket is created in the right protocol family.
In real code, you'll want to iterate over the list addrinfos
and try connecting to all of the addresses returned by getAddrInfo
. When you do that, don't forget to close the sockets that fail connecting.
Upvotes: 3