9.15. Nomes e DNS¶
Todas as páginas até agora utilizaram endereços IP numéricos – 192.168.1.20 e similares. As aplicações reais quase nunca fazem isso. Os servidores têm nomes como example.com ou api.example.com, e a aplicação procura o nome em tempo de execução para encontrar um IP para onde enviar pacotes. Essa pesquisa é o Sistema de Nomes de Domínio, ou DNS.
9.15.1. Para que resolve um nome¶
Um nome é apenas uma etiqueta. example.com não carrega qualquer informação de IP em si – tem de ser pesquisado, da mesma forma que um número de telefone é pesquisado numa lista telefónica. A infraestrutura DNS é a lista telefónica distribuída da internet, e o resultado de uma pesquisa é um ou mais endereços IP aos quais a câmara se pode ligar.
example.com -> 93.184.216.34
Um único nome resolve frequentemente para vários endereços (para balanceamento de carga, redundância geográfica, versões IPv4 e IPv6 do mesmo serviço). Qualquer um deles funciona; a aplicação escolhe um e tenta-o, recorrendo ao seguinte se esse falhar.
9.15.2. Como a pesquisa acontece¶
Quando a câmara pede por example.com:
A câmara envia um pequeno datagrama UDP (sim, UDP – veja UDP – envia um pacote e torce para o melhor) para o seu servidor DNS configurado. O endereço do servidor DNS veio da mesma troca DHCP que entregou à câmara o seu próprio IP.
O servidor DNS pode já ter a resposta em cache (foi perguntado recentemente). Se for esse o caso, responde imediatamente.
Se não, o servidor DNS percorre a hierarquia DNS global: pergunta aos servidores raiz sobre
.com, pergunta a esses servidores sobreexample.com, pergunta a esses servidores sobre o nome. Toda a travessia da árvore está oculta da câmara; a câmara vê uma consulta e uma resposta.O servidor DNS coloca o resultado em cache para a próxima vez e envia a resposta de volta à câmara como outro datagrama UDP.
A troca completa demora normalmente alguns milissegundos com uma cache quente, até cerca de cem com uma cache fria.
9.15.3. A interface Python¶
A função getaddrinfo() faz a pesquisa e devolve um endereço pronto para ser passado a um construtor de socket:
import socket
addr = socket.getaddrinfo("example.com", 80)[0][-1]
print(addr)
# ('93.184.216.34', 80)
A assinatura é getaddrinfo(host, port). O valor de retorno é uma lista de 5-tuplos (um por endereço resolvido); o [0] escolhe o primeiro e o [-1] escolhe o último campo, que é o tuplo (ip, port) que pode ser passado diretamente a um socket:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(socket.getaddrinfo("example.com", 80)[0][-1])
s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
...
A maior parte do código de câmara que comunica com um servidor remoto começa com essa única linha. Os auxiliares asyncio (asyncio.open_connection()) fazem a pesquisa internamente se for passado um nome em vez de um IP numérico, pelo que o código assíncrono normalmente não chama getaddrinfo() diretamente.
9.15.4. O que pode correr mal¶
Sem servidor DNS acessível. Se o Wi-Fi acabou de subir e a ligação é instável, a primeira chamada a
getaddrinfo()pode expirar. É levantadoOSError; tente novamente quando a ligação estiver estável.O nome não existe. Um erro de digitação ou um nome desatualizado levanta
OSErrordepois de o servidor DNS devolver «nome inexistente». O código de erro distingue isto de «servidor DNS inacessível», mas para a maioria das aplicações com câmara a política de retentar/desistir é a mesma.O endereço devolvido não funciona. O DNS pode devolver um endereço que já não aloja o serviço. A solução é recorrer à entrada seguinte na lista de resultados de
getaddrinfo(), ou pesquisar o nome novamente mais tarde (a cache DNS provavelmente terá sido atualizada entretanto).Portais cativos. Algumas redes Wi-Fi intercetam o DNS e devolvem o IP da página do portal cativo para tudo. A câmara parecerá ligar-se, mas os dados que recebe não corresponderão ao que o serviço real enviaria. Não é comum em ambientes de produção, mas acontece em redes Wi-Fi de conferências e similares.
9.15.5. O próprio nome da câmara¶
As páginas até agora pesquisaram nomes de outros dispositivos. A câmara também tem um nome próprio que anuncia à rede local quando solicita um endereço. O padrão é um identificador genérico que varia conforme a placa; network.hostname() substitui-o por algo que o resto da rede reconhece:
import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...
Defina o nome de host antes de ativar a interface, para que o nome seja enviado como parte do pedido de endereço inicial da câmara em vez de depois.
Duas coisas acontecem agora depois de a câmara se ter juntado à rede. Primeiro, a maioria dos routers domésticos regista os nomes de host a que atribuem endereços no seu próprio sistema de pesquisa local, para que outros dispositivos possam alcançar a câmara como kitchen-cam – sem precisar de conhecer o endereço numérico que o router atribuiu. (As redes empresariais podem ou não respeitar isto; o comportamento depende do router.) Segundo, a própria câmara executa um respondedor mDNS de origem, pelo que o mesmo nome também é acessível como kitchen-cam.local em qualquer rede cujos clientes entendam mDNS – o que a maioria dos sistemas operativos de secretária modernos faz.
Nota
Passe o nome de host simples para network.hostname() – apenas "kitchen-cam", sem o sufixo .local. A forma .local é o que o mDNS acrescenta no momento da pesquisa; incorporá-la no nome de host faz com que a câmara anuncie kitchen-cam.local como nome de host simples, o que não é o que nenhum dos lados da pesquisa espera.
9.15.6. Quando os nomes não chegam¶
Algumas situações em que o DNS não ajuda:
Descoberta na rede local. O DNS padrão responde a perguntas sobre nomes registados no diretório global; nada sabe sobre dispositivos no segmento local. O DNS Multicast (mDNS) é o sistema que preenche essa lacuna. Cada dispositivo participante junta-se a um grupo multicast especial na rede local e escuta consultas; quando um dispositivo pergunta por um nome que termina em
.local, o dispositivo que possui esse nome responde diretamente. Sem servidor central, sem configuração de DNS. O Bonjour nos dispositivos Apple, o Avahi no Linux, e o Windows 10+ falam todos o mesmo protocolo – razão pela qual o nomekitchen-cam.localdefinido na secção anterior resolve numa rede doméstica sem mais configurações.O lado da câmara nessa troca é o respondedor, e já está em execução. O que a câmara não tem é um resolvedor – a outra metade, que permitiria a um script perguntar à rede «onde está
printer.local?» e obter uma resposta. O código mDNS incluído é apenas respondedor (os dispositivos embebidos são tipicamente a coisa a ser encontrada, não a coisa que faz a pesquisa). Quando a descoberta tem de fluir no sentido inverso, o broadcast UDP (veja UDP – envia um pacote e torce para o melhor) é a resposta mais simples para o caso do segmento local, ou um módulo Python puro como cbrand/micropython-mdns adiciona um resolvedor completo.Nomes IPv6.
getaddrinfo()devolve resultados tanto IPv4 como IPv6 se ambos estiverem disponíveis. Escolha a família que o socket da aplicação pode utilizar.
Para a maior parte do código do lado da câmara, getaddrinfo é uma linha única no início de qualquer função que abre uma ligação de rede. Os exemplos noutras partes desta secção que correram contra "192.168.1.20" funcionariam todos da mesma forma contra um nome público como "api.example.com" – basta resolver primeiro.