O IPv6 - Happy Eyeballs II


C/C++


Em C, utiliza-se muito a função gethostbyname, da bilioteca netdb.h, para realizar consultas DNS:
        struct hostent *gethostbyname(const char *name);
         
        struct  hostent {
                char        *h_name;            /* official name of host */
                char        **h_aliases;        /* alias list */
                int         h_addrtype;         /* host address type */
                int         h_length;           /* length of address */
                char        **h_addr_list;  /* list of addresses from name server */
        };
        
        Porém essa função, assim como outras da biblioteca netdb.h, só possui suporte a IPv4. Para corrigir essa falha, foi criado a função gethostbyname2:
        
        struct hostent *gethostbyname2(const char *name, int af);
 

Como pode ser observado, foi adicionado um novo parâmetro na função chamado af (Address Family), que pode ser do tipo AF_INET para resultados exclusivamente IPv4, AF_INET6 para resultados exclusivamente IPv6 ou AF_UNSPEC para poder retornar qualquer tipo de endereço. No entanto essa função apresenta problemas caso o host em questão possua mais de 1 único IP anunciado em seu DNS.

Mais informações sobre as funções gethostbyname e gethostbyname2 podem ser encontradas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/gethostbyname.html
  2. http://tools.ietf.org/html/rfc3493

Com isso a função gethostbyname2 foi depreciada e subtituída pela função getaddrinfo:
        int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
        struct addrinfo {
                int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc.
                int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC
                int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM
                int              ai_protocol;  // use 0 for "any"
                size_t           ai_addrlen;   // size of ai_addr in bytes
                struct sockaddr *ai_addr;      // struct sockaddr_in or _in6
                char                *ai_canonname; // full canonical hostname
                struct addrinfo        *ai_next;      // linked list, next node
        };
        
 

Nota-se que a função é bem mais complexa que as anteriores, isso ocorre porque o getaddrinfo possui outras funcionalidades além das consultas DNS. O parâmetro node é o host ou IP no qual queremos nos conectar. O parâmetro service indica o tipo de serviço a ser conectado, o que pode ser uma porta (como “80” ou “22”) ou o nome do serviço (“http”, “ftp”, “telnet”, etc.). O parâmetro hints é uma estrutura de dados na qual só nos interessa o campo ai_famliy (que é o mesmo campo af da função gethostbyname2) e o campoai_socktype, que em geral será preenchido com a constante SOCK_STREAM. O parâmetro res é a estrutura de dados na qual será recebida a resposta da consulta.

Abaixo um exemplo de como utilizar a função getaddrinfo para realizar uma consulta dns:
        int main(int argc, char *argv[])
        {
                struct addrinfo hints, *res, *p;
                int status;
                char ipstr[INET6_ADDRSTRLEN];
         
                if (argc != 2) {
                        fprintf(stderr, "Usage: %s hostname\n", argv[0]);
                        return 1;
                }
         
                memset(&hints, 0, sizeof hints);
                hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
                hints.ai_socktype = SOCK_STREAM;
         
                if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
                        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
                        return 2;
                }
         
                for(p = res;p != NULL; p = p->ai_next) {
         
                        void *addr;
                         
                        if (p->ai_family == AF_INET) {
                                return 1;
                         
                        } else {
                         
                                struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
                                addr = &(ipv6->sin6_addr);
                                 
                                /* convert the IP to a string and print it: */
                                inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
                                 
                                printf("Hostname: %s\n", argv[1]);
                                printf("IP Address: %s\n", ipstr);
                                 
                        }
                }
         
                freeaddrinfo(res); // free the linked list
                 
                return 0;
        }
 

Mais informções sobre a função getaddrinfo podem ser encontradas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/getaddrinfo.html
  2. http://pubs.opengroup.org/onlinepubs/009619199/getad.htm

Os sockets em C estão definidos na biblioteca sys/socket.h. Para se criar um socket é utilizado a seguinte função:
        int socket(int domain, int type, int protocol);
 

Apesar de parecer simples, essa função é um pouco confusa, pois todos os seus parâmetros de entrada são constantes pré-definidas. O parâmetro domain é o mesmo parâmetro af da função gethostbyname e o parâmetro ai_family da função getaddrinfo, ou seja, indica o tipo de endereço utilizado pelo socket. O parâmetro type é o mesmo parâmetro ai_socktype da função getaddrinfo, que indica o tipo de socket a ser utilizado. O parâmetro protocol indica o tipo de protocolo a ser utilizado na conexão pelo socket (como tcp, udp, etc.). Note que esse parâmetro é parecido com o parâmetro service da função getaddrinfo, no entantoservice é uma string, enquanto protocol é um número do tipo int. Normalmente no parâmetro protocol é utilizado o valor IPPROTO_TCP, para indicar que a conexão deverá ser via TCP, porém se deixado seu valor como 0, a própria função ficará encarregada de decidir qual o melhor protocolo a ser utilizado para o tipo endereço e socket utilizado.

É importante observar que essa função não retorna o socket em si, mas um número que representa o id do socket criado. Em quase todas as funções de socket o procedimento é o mesmo, não há contato direto com o socket, apenas são passados os ids dos sockets dentro das funções que irão realizar as operações.

Para se conectar a um host utilizando o socket criado devemos utilizar a função connect:
        
        int connect(int socket, const struct sockaddr *address, socklen_t address_len);
        
        O parâmetro socket é o mesmo socket id que foi gerado na função socket. O parâmetro address é uma struct retornada pela função getaddrinfo, dentro da struct addrinfo->ai_addr O parâmetro address_len também é um parâmetro retornado pela função getaddrinfo, dentro da struct addrinfo->ai_addrlen. A struct sockaddr pode ser de vários  tipos:
        
        struct sockaddr {
                unsigned short    sa_family;    // address family, AF_xxx
                char              sa_data[14];  // 14 bytes of protocol address
        };
        
        
        // IPv4 AF_INET sockets:
        
        struct sockaddr_in {
                short            sin_family;   // e.g. AF_INET, AF_INET6
                unsigned short   sin_port;     // e.g. htons(3490)
                struct in_addr   sin_addr;     // see struct in_addr, below
                char             sin_zero[8];  // zero this if you want to
        };
        
        struct in_addr {
                unsigned long s_addr;          // load with inet_pton()
        };
        
        
        // IPv6 AF_INET6 sockets:
        
        struct sockaddr_in6 {
                u_int16_t       sin6_family;   // address family, AF_INET6
                u_int16_t       sin6_port;     // port number, Network Byte Order
                u_int32_t       sin6_flowinfo; // IPv6 flow information
                struct in6_addr sin6_addr;     // IPv6 address
                u_int32_t       sin6_scope_id; // Scope ID
        };
        
        struct in6_addr {
                unsigned char   s6_addr[16];   // load with inet_pton()
        };
        
        
        // General socket address holding structure, big enough to hold either
        // struct sockaddr_in or struct sockaddr_in6 data:
        
        struct sockaddr_storage {
                sa_family_t  ss_family;     // address family
        
                // all this is padding, implementation specific, ignore it:
                char      __ss_pad1[_SS_PAD1SIZE];
                int64_t   __ss_align;
                char      __ss_pad2[_SS_PAD2SIZE];
        };
        
        Abaixo um exemplo do uso da função connect:
        
        int sd;
        int rc;
        struct addrinfo ai_hints, *ai;
        
        /* create hints for getaddrinfo for stream socket */
        memset(&ai_hints, 0, sizeof(ai_hints));
        ai_hints.ai_family = AF_INET6;
        ai_hints.ai_socktype = SOCK_STREAM;
        ai_hints.ai_protocol = IPPROTO_TCP;
        /* get list of addresses for nodename from DNS */
        rc = getaddrinfo(szNodename, szService, &ai_hints, &ai);
        if (rc != 0)
        {
        strcpy(szError, "getaddrinfo() failed: ");
                strcat(szError, gai_strerror(rc));
                return -1;
        }
        /* create socket for this address */
        sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if (sd < 0)
        {
        /* fail */
        strcpy(szError, "socket() failed, ");
        strcat(szError, strerror(errno));
        return -1;
        }
        rc = connect(sd, ai->ai_addr, ai->ai_addrlen);
        
 

É importante lembrar que ao iniciar a função connect o programa irá travar sua execução  até que a conexão seja estabelecida. Para a execução do Happy Eyeballs é necessário que o programa continue sua execução mesmo após a chamada da função connect. A forma mais simples de se fazer isso é tornando o socket non-blocking. Dessa forma o programa continuará sua execução mesmo após um connect.

Mais informações sobre sockets e suas funções podem ser encontradas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/socket.html
  2. http://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html
  3. http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/socket.h.html

Para tornar o socket  non-blocking pode-se utilizar a função ioctl, da biblioteca stropts.h:
        int ioctl(int fildes, int request, ... /* arg */);
        
        Note que essa é uma função mais avançada, portanto deve ser utilizado com cuidado. O parâmetro fildes (File Descriptor) indica o id do arquivo a ser modificado (no nosso caso é o socket, portanto seu id). O parâmetro request indica o comando que deverá ser executado, no nosso caso seria a constante FIONBIO. O parâmetro arg são configurações ou informações adicionais específicos da operação e do tipo de fildes utilizado. Um exemplo de como utilizar a função ioctl:
        
        int sd;
        int opt = 1;
        sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        ioctl(sd, FIONBIO, &opt);
        
        Note que nesse caso foi utilizado um parâmetro chamado opt de valor 1. A função ioctl, quando utilizada com o parâmetro FIONBIO, torna o socket non-blocking caso o parâmetro arg seja diferente de zero. Portanto no exemplo a função está tornando o socket criado em um socket non-blocking. Para retornar o socket em modo blocking basta retirar a flag setada anteriormente, utilizando o parâmetro arg com valor igual a zero:
        
        int sd;
        int opt = 1;
        sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        // Seta socket non-blocking
        ioctl(sd, FIONBIO, &opt);
        // Seta socket blocking
        opt = 0;
        ioctl(sd, FIONBIO, &opt);
 

Mais informações sobre a função ioctl podem ser encontradas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/ioctl.html

Outra forma de se tornar o socket non-blocking é através da função fcntl, da biblioteca fcntl.h:
        int fcntl(int fildes, int cmd, ... /* arg */ );
 

Novamente, essa funçao deve ser utilizada com cuidado, pois mexe nas propriedades  do arquivo que queremos modificar. O parâmetro fildes é o mesmo da função ioctl, ou seja, o identificador do arquivo alvo (socket). O parâmetro cmd é similar ao parâmetro request da função ioctl, porém com constantes diferentes, o mesmo se aplica ao parâmetro arg. Abaixo um exemplo de como utilizar a função fcntl para tornar o socketnon-blocking:
        int sd;
        sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        int flags = fcntl(sd, F_GETFL, 0);
        fcntl(sd, F_SETFL, flags | O_NONBLOCK);
 

Note que sua utilização é um pouco diferente da ioctl, pois é necessário executar a função duas vezes: uma para adquirir o parêmetro de flags do socket e outro para setar a flag indicando que o socket será non-blocking. Mais informações sobre a função fcntl podem ser acessadas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html

Quando o socket está em modo non-blocking, é necessário verificar de tempos em tempos se a conexão foi estabelecida. Existem várias formas de se verificar isso, porém para o Happy Eyeballs uma função interessante é a getpeername, da biblioteca socket.h:
        int getpeername(int socket, struct sockaddr *address, socklen_t *address_len);
 

Como é possível notar, esta função possui os mesmos parâmetros de entrada que a função connect, ou seja, podemos utilizar as mesmas entradas utilizadas na função connect na função getpeername, como no exemplo abaixo:
        /* Faz as consultas DNS */
        
        sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        rc = connect(sd, ai->ai_addr, ai->ai_addrlen);
        // Espera estabelecer a conexão
        while (!connected) {
        rc = getpeername(sd, ai->ai_addr, ai->ai_addrlen);
        if (rc < 0) {
                connected = TRUE;
        }
        }
        // Conexão estabelecida
 

Mais informações sobre a função getpeername podem ser encontradas em:
  1. http://pubs.opengroup.org/onlinepubs/009695399/functions/getpeername.html
  2. http://pubs.opengroup.org/onlinepubs/7908799/xns/getpeername.html

Java


No java devemos tomar certo cuidado, pois o tratamento de IPs é dada pela classeInetAddress (java.net.InetAddress). Esta classe possui 2 implementações diretas: Inet4Adress para IPv4 eInet6Address para IPv6. A documentação oficial das classes pode ser encontrada em:
  1. http://docs.oracle.com/javase/1.4.2/docs/api/java/net/InetAddress.html
  2. http://docs.oracle.com/javase/1.4.2/docs/api/java/net/Inet4Address.html
  3. http://docs.oracle.com/javase/1.4.2/docs/api/java/net/Inet6Address.html

Basicamente, a classe InetAddress é a responsável por armazenar e tratar os endereços IP. Além disso ela disponibiliza métodos de análises de escopo do IP, que estão listados a seguir:
  1. public boolean isAnyLocalAddress()
  2. public boolean isLoopbackAddress()
  3. public boolean isLinkLocalAddress()
  4. public boolean isSiteLocalAddress()
  5. public boolean isMCGlobal()
  6. public boolean isMCNodeLocal()
  7. public boolean isMCLinkLocal()
  8. public boolean isMCSiteLocal()
  9. public boolean isMCOrgLocal()

Esses métodos são reponsáveis por determinar o escopo do IP em questão. Eles são úteis se for necessário uma verificação prévia do IP, principalmente IPv6, onde são criados endereços link-locais automaticamente.

Abaixo um exemplo de como adquirir um IP através de um hostname:
        InetAddress address = InetAddress.getByName(“hostname”);
        System.out.println(address.getHostAddress);
        
        Neste caso o java irá buscar um IP retornado na consulta DNS . Porém ele irá retornar apenas um dos IPs disponíveis. Portanto se quisermos utilizar o Happy Eyeballs precisamos obter todos os IPs retornados pela consulta. Isso pode ser feito da seguinte forma:
        
        InetAddress[] addressArray = InetAddress.getAllByName(“hostname”);
        for (InetAddress address : addressArray) {
                System.out.println(address.getHostAddress);
        }
 

Dessa forma todos os IPs são armazenados no array addressArray.

Outra observacão importante é com relação aos Sockets, principalmente no quesito blocking/non-blocking. Apesar do java implementar a classe Socket (java.net.Socket), para utilizar sockets non-blocking devemos usar a classe SocketChannel (java.nio.channels.SocketChannel) e setar sua propriedade interna configureBlocking para false. A documentação oficial dos sockets pode ser encontrada em:
  1. http://docs.oracle.com/javase/1.4.2/docs/api/java/net/Socket.html
  2. http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/channels/SocketChannel.html

Abaixo um exemplo de como criar um socket simples para tentar se conectar a algum host:
        Socket socket = new Socket(“hostname”, “porta”);
        socket.connect();
        // Faz o que precisa com o socket
        socket.close();
 

Esse exemplo mostra dois pontos importantes. O primeiro é que neste caso os IPs são resolvidos de forma transparente, ou seja, em nenhum momento foi necessário definir um IP para se conectar ao host. O segundo ponto implica justamente no problema que isso pode gerar quando estamos lidando com IPv6, pois se o método connect do socket não implementar o Happy Eyeballs teremos o problema descrito neste artigo.

Então para o Happy Eyeballs funcionar no java temos de utilizar um IP para conectar ao socket:
        InetAddress address = InetAddress.getByName(“hostname”);
        Socket socket = new Socket(address, “porta”);
        socket.connect();
        // Faz o que precisa com o socket
        socket.close();
 

Porém, como já foi dito, a classe Socket não tem suporte a sockets non-blocking (diretamente). Para corrigir esse problema utilizamos a classe SocketChannel:
        InetAddress address = InetAddress.getByName(“hostname”);
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress(address, 80));
 

Desta forma será criado um socket non-blocking. Um detalhe muito importante a ser notado é que o programa continuará sua execução após a chamada do método connect, ou seja, ele não esperará a conexão ser estabelecida para continuar sua execução (pois o socket está configurado como non-blocking). Assim é necessário fazer uma verificação de conectividade antes de começar a trabalhar em cima do socket:
        InetAddress address = InetAddress.getByName(“hostname”);
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress(address, 80));
        // Espera se conectar
        while (!channel.finishConnect()) {
                // Espera um tempo
        }
        // Faz o que precisa com o socket
        socket.close();
 

É extamente isso que queremos para o Happy Eyeballs, pois enquanto esperamos uma conexão ser realizada, podemos tentar se conectar aos demais IPs disponíveis. Mas ao mesmo tempo precisamos de um IPv6 e um IPv4. Para isso utilizamos o método InetAddress.getAllByName para obter tanto as entradas IPv6 quanto IPv4:
        // Pega todos os IPs disponíveis para o hostname em específico
        InetAddress[] addressArray = InetAddress.getAllByName(“hostname”);
        for (InetAddress address : addressArray) {
        // Faz alguma coisa com o IP
        }
 


Como devemos tentar conectar primeiro a um IPv6, devemos distinguir quais IPs retornados são IPv6 e quais são IPv4. Para confirmar se o endereço é um IPv6 ou IPv4, utilizamos as classes Inet6Address e Inet4Address e fazemos uma comparação com instanceof:
        // Pega todos os IPs disponíveis para o hostname em específico
        InetAddress[] addressArray = InetAddress.getAllByName(“hostname”);
        for (InetAddress address : addressArray) {
        if (address instanceof Inet6Address) {
                        // O endereço é IPv6
        }
        if (address instanceof Inet4Address) {
                // O endereço é IPv4
        }
        }
 

php


O php não possui a função getaddrinfo, porém ele possui a função gethostbyname:
        string gethostbyname ( string $hostname );
        
 
Além disso, ele também possui outra função chamada gethostbynamel:
        array gethostbynamel ( string $hostname );
        
 
A diferença básica entre os dois é que na função gethostbyname é retornado apenas um único endereço IP. Já na funçao gethostbynamel é retornado um array contendo todos os endereços encontrados. Porém em ambos os casos só são retornados endereços IPv4. Mais informações sobre estas funções podem ser encontradas em:
  1. http://www.php.net/manual/en/function.gethostbyname.php
  2. http://www.php.net/manual/en/function.gethostbynamel.php

Por isso para obter todos os endereços IP, incluindo os endereços IPv6, devemos utilizar a funçãodns_get_record, que funciona como a função getaddrinfo:
        array dns_get_record ( string $hostname [, int $type = DNS_ANY [, array &$authns [, array &$addtl ]]] );
 

Apesar de parecer complicada, para a implementação do Happy Eyeballs não é necessário adicionar muitos parâmetros a função, somente o tipo de consulta dns a ser feita, que são do tipo DNS_A (para endereços IPv4) e DNS_AAAA (para endereços IPv6):
        $host = 'www.teste.com.br';
        $Arecord = dns_get_record($host, DNS_A);
        $AAAArecord = dns_get_record($host, DNS_AAAA);
 

A função dns_get_record retorna um array com várias informações, porém apenas 2 nos interessam:
  1. type: pode ser de vários tipos, mas nos interessam apenas os tipo A e AAAA para poder identificar o que é IPv6 e o que é IPv4;
  2. ip ou ipv6: dado que vem atrelado ao type quando sua resposta é A ou AAAA. Dentro dele contém qual o IP retornado.

Mais informações sobre a função dns_get_record podem ser encontradas em:
  1. http://php.net/manual/en/function.dns-get-record.php

Para se criar um socket em php, utiliza-se a função socket_create:
        resource socket_create ( int $domain , int $type , int $protocol );
 

Note que todos os parâmetros de entradas são constantes de configuração pré definidas. O parâmetrodomain indica qual o protocolo do endreço utilizado (Address Family), podendo ser AF_INET no caso de IPv4 ou AF_INET6 no caso de IPv6. O parâmetro type indica o tipo de protocolo utilizado, que normalmente é do tipo SOCK_STREAM ou SOCK_RAW. O parâmetro protocol indica o tipo de protocolo IP a ser utilizado, comotcp, udp ou icmp. Abaixo temos um exemplo de como criar um socket:
        $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
 

Para se conectar a um host utilizando o socket é utilizado a função socket_connect:
        bool socket_connect ( resource $socket , string $address [, int $port = 0 ] );
 

O parâmetro socket é o socket que foi criado na função socket_create. O parâmetro address é o endereço no qual queremos nos conectar. O parâmetro port é a porta na qual o socket irá se conectar. Abaixo utilizaremos o socket criado para se conectar a um IP:
        $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
        socket_connect($socket, ‘2001:12ff:0:4::6’, 80)
 

Para setar o socket php para non-blocking basta utilizar a função socket_set_nonblock:
        bool socket_set_nonblock ( resource $socket );
 

Onde o parâmetro socket é o socket no qual queremos tornar non-blocking. Para voltar o socket a formablocking, utilizamos a função socket_set_block:
        bool socket_set_block ( resource $socket );
 

Abaixo um exemplo de como utilizar um socket non-blocking:
        $socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
        socket_set_nonblock($socket);
        while (!($connected = @socket_connect($socket,‘2001:12ff:0:4::6’, 80))) {
                // Faz alguma coisa
         }
        // Conectou-se ao servidor
        socket_set_block($socket);
 

Mais informações sobre sockets em php podem ser encontrados em:
  1. http://php.net/manual/en/book.sockets.php
  2. http://www.php.net/manual/en/function.socket-create.php
  3. http://www.php.net/manual/en/function.socket-connect.php
  4. http://www.php.net/manual/en/function.socket-set-block.php
  5. http://www.php.net/manual/en/function.socket-set-nonblock.php

python


Em python, tanto a consulta de DNS quanto as operações de sockets estão dentro da biblioteca socket. Além disso podemos verificar se o sistema utilizado possui suporte a IPv6, através da propriedade:
        socket.has_ipv6
 

Para realizar consultas DNS, temos a função gethostbyname:
        socket.gethostbyname(hostname)
 

Temos também uma versão mais detalhada, chamada gethostbyname_ex:
        socket.gethostbyname_ex(hostname)
 

A diferença básica entre as duas funções são as informações retornadas. Enquanto na funçãogethostbyname é retornado apenas o endereço IP na forma de string, na função gethostbyname_ex também são retornados a lista de nomes e a lista de IPs disponíveis. No entando nenhuma das duas funções tem suporte a IPv6, portanto não servem para implementar o algoritmo do Happy Eyeballs.

Para realizar consultas DNS que incluem o IPv6, usamos a função getaddrinfo:
        socket.getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0)
 

Seu funcionamento é o mesmo da linguagem C, portanto as constantes são as mesmas, com o detalhe de que essas constantes estão dentro da biblioteca socket do python. Um exemplo abaixo de como utilizar a função getaddrinfo:
        socket.getaddrinfo(“ipv6.br", 80, socket.AF_INET6, socket.SOCK_STREAM, socket.SOL_TCP)
 

A função acima irá listar os IPv6 disponíveis para uma possível conexão tcp via stream socket na porta 80 do site ipv6.br. O retorno da função getaddrinfo é do tipo:
        (family, socktype, proto, canonname, sockaddr)
 

Para o Happy Eyeballs precisamos apenas do campo sockaddr, que indicam o endereço IP, sendo do tipo(address, port) no caso do IPv4 (AF_INET) e (address, port, flow info, scope id) no caso do IPv6 (AF_INET6). Em ambos os casos utilizaremos apenas o campo address retornado, que é o que realmente precisamos. Veja o exemplo abaixo:
        >>> socket.getaddrinfo("ipv6.br", 80, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.SOL_TCP)
        [(10, 1, 6, '', ('2001:12ff:0:4::22', 80, 0, 0)), (2, 1, 6, '', ('200.160.4.22', 80))]
 

Nesse caso a consulta retorna tanto os endereços IPv4 quanto os endereços IPv6, pois o parâmetro family está setado como AF_UNSPEC, o que faz com que ele aceite qualquer tipo de protocolo como resposta. Mais informações sobre a função getaddrinfo podem ser encontradas em:
  1. http://docs.python.org/library/socket.html#socket.getaddrinfo

Para criar um socket em python basta utilizar a função socket da biblioteca socket:
        socket.socket([family[, type[, proto]]])
 

Sua utilização é bem simples:
        s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
 

Nesse caso o socket criado é atribuído a variável s. É importante observar que pela definição do socket, faltam duas informações essenciais: o endereço IP e a porta na qual tentaremos conectar. Esses parâmetros são indicados quando utilizamos a função connect:
        socket.connect(address)
 


Para setar o socket como non-blocking basta setar sua propriedade setblocking(valor) para 0.
        socket.setblocking(flag)
 

perl


O perl possui um problema grave com relação ao IPv6, pois sua biblioteca padrão de sockets (IO::Socket::INET) não possui suporte a IPv6. Além disso, o perl utiliza um sistema de módulos para serem adicionados e incorporados a ele, porém não existe uma padronização oficial para isso, o que leva a existirem vários módulos diferentes que implementam uma mesma funcionalidade. É o que acontece no caso do módulo que suporta sockets IPv6. Nesta artigo vamos apenas abordar o módulo indicado no próprio site do perl (http://www.perl.org/about/whitepapers/perl-ipv6.html), a nova biblioteca de sockets  (IO::Socket::IP), que suporta tanto IPv4 quanto IPv6.

Javascript


Javascript é um caso especial, pois não é possível realizar consultas DNS diretamente utilizando apenas javascript. Uma solução possível é criar um servidor que faça a consulta em seu lugar e tentar se conectar segundo o algoritmo do Happy Eyeballs. Porém essa prática não é recomendada, pois além de necessitar fazer uma conexão extra fora do escopo, ele fica dependente dessa resposta, além de necessitar de um servidor exclusivo para isso. 

Links Úteis

  1. Exemplo de Happy Eyeballs em C: http://www.ipv6forum.com/ipv6_enabled/DNS.php
  2. Explicações detalhadas sobre o funcionamento do Dual Stack: http://owend.corp.he.net/ipv6/
  3. Happy Eyeballs no squid: http://squidproxy.wordpress.com/2012/07/14/happy-eyeballs/
  4. Hampering Eyeballs: https://labs.ripe.net/Members/emileaben/hampered-eyeballs
  5. Comparação entre implementações do Happy Eyeballs: http://knowipv6.digitalelement.com/?p=66
  6. Entendendo o Happy Eyeballs: http://www.ipjforum.org/?p=378



Fonte: IPv6.br

Comentários

Postagens mais visitadas deste blog

O IPv6 - Alocação de endereços

Característica do Protocolo FTP

A camada Aplicação - Modelo OSI