9.15. Nomes e DNS¶
Toda página até aqui usou endereços IP numéricos – 192.168.1.20 e similares. Aplicações reais quase nunca fazem isso. Servidores recebem nomes como example.com ou api.example.com, e a aplicação resolve o nome em tempo de execução para encontrar um IP ao qual enviar pacotes. Essa resolução é o Domain Name System, ou DNS.
9.15.1. O que um nome resolve¶
Um nome é apenas um rótulo. example.com não carrega nenhuma informação de IP em si – ele precisa ser resolvido, da mesma forma que um número de telefone é procurado em uma lista telefônica. A infraestrutura de DNS é a lista telefônica distribuída da internet, e o resultado de uma resolução é um ou mais endereços IP aos quais a câmera pode se conectar.
example.com -> 93.184.216.34
Um único nome muitas vezes resolve 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 o tenta, recorrendo ao próximo se aquele falhar.
9.15.2. Como a resolução acontece¶
Quando a câmera pede por example.com:
A câmera envia um pequeno datagrama UDP (sim, UDP – veja UDP – envie um pacote e torça pelo melhor) ao seu servidor DNS configurado. O endereço do servidor DNS veio da mesma troca DHCP que entregou à câmera seu próprio IP.
O servidor DNS pode já ter a resposta em cache (ele foi consultado recentemente). Se for o caso, ele responde imediatamente.
Se não, o servidor DNS percorre a hierarquia global do DNS: pergunta aos servidores raiz sobre
.com, pergunta a esses servidores sobreexample.com, pergunta a esses servidores sobre o nome. Toda essa caminhada pela árvore fica oculta da câmera; a câmera vê uma consulta e uma resposta.O servidor DNS armazena o resultado em cache para a próxima vez e envia a resposta de volta à câmera como outro datagrama UDP.
A troca inteira normalmente leva alguns milissegundos com um cache quente, e até uma centena, mais ou menos, com um cache frio.
9.15.3. A interface Python¶
A função getaddrinfo() faz a resolução e retorna um endereço pronto para ser entregue 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 tuplas de 5 elementos (uma por endereço resolvido); o [0] escolhe a primeira e o [-1] escolhe o último campo, que é a tupla (ip, port) que pode ser entregue 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âmera que conversa com um servidor remoto começa com essa única linha. Os auxiliares de asyncio (asyncio.open_connection()) fazem a resolução internamente se um nome for passado em vez de um IP numérico, então o código assíncrono normalmente não chama getaddrinfo() diretamente.
9.15.4. O que pode dar errado¶
Nenhum servidor DNS acessível. Se o Wi-Fi acabou de subir e o link está instável, a primeira chamada a
getaddrinfo()pode expirar. UmOSErroré gerado; tente novamente quando o link estiver estável.Nome não existe. Um erro de digitação ou um nome desatualizado gera um
OSErrordepois que o servidor DNS retorna “nenhum nome desse tipo”. O código de erro distingue isso de “servidor DNS inacessível”, mas, para a maioria das aplicações de câmera, a política de repetir/desistir é a mesma.O endereço retornado não funciona. O DNS pode retornar um endereço que não hospeda mais o serviço. A correção é recorrer à próxima entrada na lista de resultados de
getaddrinfo(), ou resolver o nome novamente mais tarde (o cache de DNS provavelmente já terá sido atualizado a essa altura).Portais cativos. Algumas redes Wi-Fi interceptam o DNS e retornam o IP da página do portal cativo para tudo. A câmera parecerá conectar, mas os dados que ela recebe de volta não corresponderão ao que o serviço real enviaria. Não é comum em ambientes implantados, mas é algo que acontece em Wi-Fi de conferências e redes similares.
9.15.5. O próprio nome da câmera¶
As páginas até aqui resolveram nomes de outros dispositivos. A câmera também tem um nome próprio que ela anuncia à rede local quando pede um endereço. O padrão é um identificador genérico que varia conforme a placa; network.hostname() o substitui por algo que o restante da rede vá reconhecer:
import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...
Defina o hostname antes de subir a interface, para que o nome saia como parte da requisição de endereço inicial da câmera, e não depois.
Duas coisas passam a acontecer assim que a câmera se junta à rede. Primeiro, a maioria dos roteadores domésticos registra os hostnames para os quais entrega endereços em sua própria resolução local, de modo que outros dispositivos podem alcançar a câmera como kitchen-cam – sem precisar saber o endereço numérico que o roteador acabou atribuindo. (Redes corporativas podem ou não honrar isso; o comportamento depende do roteador.) Segundo, a própria câmera executa um respondedor mDNS de fábrica, então o mesmo nome também fica acessível como kitchen-cam.local em qualquer rede cujos clientes entendam mDNS – o que a maioria dos sistemas operacionais de desktop modernos faz.
Nota
Passe o hostname puro para network.hostname() – apenas "kitchen-cam", sem o sufixo .local. A forma .local é o que o mDNS adiciona no momento da resolução; embuti-la no hostname faz a câmera anunciar kitchen-cam.local como um hostname comum, o que não é o que nenhum dos lados da resolução espera.
9.15.6. Quando nomes não bastam¶
Algumas situações em que o DNS não ajuda:
Descoberta na rede local. O DNS padrão responde perguntas sobre nomes registrados no diretório global; ele não sabe nada sobre dispositivos no segmento local. O Multicast DNS (mDNS) é o sistema que preenche essa lacuna. Cada dispositivo participante se junta a um grupo multicast especial na rede local e escuta consultas; quando um dispositivo pede por um nome terminado em
.local, o dispositivo dono daquele nome responde diretamente. Sem servidor central, sem configuração de DNS. O Bonjour em dispositivos Apple, o Avahi no Linux e o Windows 10+ falam todos o mesmo protocolo – razão pela qual o nomekitchen-cam.localque a seção anterior configurou resolve em uma rede doméstica sem nada extra configurado.O lado da câmera nessa troca é o respondedor, e ele já está em execução. O que a câmera não tem é um resolvedor – a outra metade, que permitiria a um script perguntar à rede “onde está
printer.local?” e obter uma resposta de volta. O código mDNS incluído é somente respondedor (dispositivos embarcados são tipicamente aquilo que se encontra, não aquilo que faz a busca). Quando a descoberta precisa fluir na outra direção, o broadcast UDP (veja UDP – envie um pacote e torça pelo melhor) é a resposta mais simples para o caso do segmento local, ou um módulo em Python puro como cbrand/micropython-mdns adiciona um resolvedor completo.Nomes IPv6.
getaddrinfo()retorna resultados IPv4 e IPv6 se ambos estiverem disponíveis. Escolha a família que o socket da aplicação pode usar.
Para a maior parte do código do lado da câmera, getaddrinfo é uma única linha no topo de qualquer função que abra uma conexão de rede. Os exemplos em outras partes desta seção que rodaram contra "192.168.1.20" funcionariam todos da mesma forma contra um nome público como "api.example.com" – basta resolver primeiro.