Resolução de Nomes: DNS na Era da Nuvem
por Frank de Alcantara em 24/08/2026
Os quatro artigos anteriores construíram o caminho até uma máquina. Nenhum deles perguntou como se descobre qual máquina.
Índice da Série: Redes para Engenharia de Software
- 1. A Pilha TCP/IP e a Física da Rede
- 2. Da Rede Local à Web: Dispositivos, Topologias e Serviços
- 3. Camada IP: Endereçamento, Sub-redes e Encaminhamento
- 4. Transporte: TCP, UDP e QUIC
- 5. Resolução de Nomes: DNS na Era da Nuvem (Você está aqui)
A pergunta parece administrativa e não é. O sistema que a responde opera continuamente desde 1987 e atravessa fronteiras administrativas, provedores e continentes. Essa distribuição permite que partes inteiras falhem sem apagar todo o espaço de nomes, mas também introduz dependências que fazem uma aplicação saudável parecer indisponível. As duas propriedades saem do mesmo mecanismo: o DNS aceita respostas temporariamente antigas para continuar respondendo, e expressa esse compromisso em um número que quase ninguém revisa.
O número tem nome, TTL, e este artigo é em boa parte sobre a aritmética dele. A leitora vai sair sabendo calcular a taxa de acerto de um cache, o tempo esperado para uma mudança propagar, a carga que uma redução de TTL despeja sobre a infraestrutura autoritativa e, principalmente, quanto tempo um serviço fica indisponível depois que a verificação de saúde já detectou o problema.
Continuamos com a régua do Artigo 1: $\text{RTT} = 76{,}65$ ms entre São Paulo e Ashburn quando precisarmos de uma rota longa, e o piso físico da Tabela 2 daquele artigo como critério de falsificação de qualquer afirmação de latência.
1. A hierarquia e a delegação
O espaço de nomes do DNS é uma árvore invertida. A raiz, escrita como um ponto solitário, tem filhos que são os domínios de topo, como com, org e br; estes têm filhos que são os domínios registrados, como example.com; e assim por diante. Um nome plenamente qualificado é o caminho da folha até a raiz, lido da esquerda para a direita: www.example.com.. O ponto final existe e é parte do nome, ainda que quase ninguém o escreva.
O que faz o sistema escalar não é a árvore: é a delegação. Nenhum servidor conhece todos os nomes. Os servidores raiz sabem apenas quem responde por cada domínio de topo; os servidores de com sabem apenas quem responde por cada domínio registrado sob eles; e o servidor autoritativo de example.com é quem de fato conhece os endereços. Cada nível conhece somente o seguinte. Em 5 de agosto de 2026, os treze identificadores de servidores raiz correspondiam a $2002$ instâncias operacionais, mantidas por doze organizações independentes. O número de identificadores é pequeno; a implantação física não é.
Há dois papéis que a leitora precisa separar, porque confundi-los produz diagnósticos errados a vida inteira.
O servidor autoritativo é quem tem a resposta. Ele publica os dados de uma zona, responde apenas sobre ela, e não faz consultas em nome de ninguém.
O resolvedor recursivo é quem procura a resposta. Ele recebe a pergunta do cliente, percorre a hierarquia de cima para baixo, guarda o que aprendeu em cache e devolve o resultado. É o resolvedor do seu provedor, ou o resolvedor público que a leitora configurou, ou o resolvedor dentro do seu agrupamento Kubernetes, que o Artigo 14 vai tratar.
E há um terceiro, quase invisível: o resolvedor cliente, ou stub resolver, a biblioteca do sistema operacional que faz a pergunta e a encaminha ao resolvedor recursivo.
A Figura 1 mostra o caminho completo de uma consulta fria, com os saltos e os tempos que a Seção 4 vai calcular.
Figura 1: uma resolução fria custa 93 ms no modelo. O cache do resolvedor reduz o custo para 3 ms; o cache da própria máquina pode reduzi-lo para menos de 1 ms.
Note o que a figura não mostra, porque não existe: nenhuma etapa desse caminho contata todos os servidores, nenhuma consulta é transmitida em difusão, e nenhum componente tem visão global. O DNS escala porque cada participante sabe muito pouco.
2. Tipos de registro e o que cada um decide
Uma zona é um conjunto de registros, e cada registro é uma tupla de nome, tipo, classe, TTL e dados. A classe é praticamente sempre IN, de Internet, e as outras existem por razões históricas que não interessam aqui. O tipo é o que decide o significado.
A Tabela 1 reúne os tipos que a engenheira de software encontra no dia a dia, com a decisão de engenharia que cada um materializa.
| Tipo | O que carrega | Decisão de engenharia que ele materializa |
|---|---|---|
A |
um endereço IPv4 | para qual máquina o tráfego vai |
AAAA |
um endereço IPv6 | o mesmo, na outra família de endereços |
CNAME |
outro nome | terceiriza a resposta, ao custo de uma resolução adicional |
NS |
os autoritativos de uma zona | onde a delegação aponta |
SOA |
parâmetros da zona | quem é o autoritativo primário e os tempos de sincronização |
MX |
destino de correio, com prioridade | para onde vai a mensagem, e em que ordem tentar |
TXT |
texto arbitrário | prova de posse, política de correio, verificação de terceiros |
SRV |
serviço, porta e alvo | descoberta de serviço com porta, usada por muitos protocolos |
CAA |
quais autoridades podem emitir certificado | restrição de emissão, verificada pela autoridade |
PTR |
nome a partir de endereço | resolução reversa, usada em registro e em reputação de correio |
Tabela 1: os tipos que a engenheira de software encontra. A coluna da direita é a razão de o tipo existir; a do meio é apenas o formato.
Um deles merece parágrafo próprio, porque é a fonte de um erro que a leitora vai cometer, ou vai revisar, mais cedo ou mais tarde.
O CNAME instrui o resolvedor a continuar a busca em outro nome. É extremamente útil: permite apontar www.example.com para o nome que o provedor de nuvem gerencia, e o provedor troca endereços sem que ninguém precise editar a zona do cliente. O preço depende de quanto da nova hierarquia já está em cache, e a Seção 4 estabelece um limite superior explícito para esse custo.
A armadilha está na regra da RFC 1034: se existe um CNAME em um nó, nenhum outro dado pode existir naquele nó. A razão é semântica, não de implementação: um CNAME afirma que o nome é um apelido, e um apelido não pode ter, ele próprio, endereço, servidor de correio ou qualquer outra coisa.
A consequência morde no ápice da zona, isto é, em example.com sem prefixo nenhum. O ápice é obrigado a ter um SOA e ao menos dois NS, porque é ali que a delegação chega. Logo, o ápice não pode ter um CNAME. E é exatamente ali que todo mundo quer apontar para o balanceador do provedor.
A saída da indústria foi criar extensões proprietárias, com nomes como ALIAS e ANAME, que se comportam como CNAME na interface de configuração e resolvem o alvo do lado do servidor autoritativo, devolvendo um A ou AAAA comum ao cliente. Funciona, resolve o problema real, e tem duas consequências que a leitora precisa registrar: a configuração deixa de ser portável entre provedores, e o TTL efetivo passa a ser decidido por uma composição entre o TTL do alvo e a política do provedor, que nem sempre está documentada.
3. Cache e TTL: o compromisso quantificado
Cada registro carrega um TTL, de Time To Live (tempo de vida), em segundos. Ele é uma instrução do autoritativo para quem armazenar a resposta: pode usar este valor por até tantos segundos sem me perguntar de novo.
A formulação que interessa é outra, e é desconfortável: o TTL é a janela de inconsistência que a organização aceita. Durante ele, resolvedores no mundo inteiro podem estar respondendo um valor que o dono da zona já mudou, e não há mecanismo nenhum para avisá-los. O DNS não tem invalidação de cache. Tem apenas expiração.
Vamos quantificar as duas pontas do compromisso.
A taxa de acerto. Suponha consultas chegando a um resolvedor segundo um processo de Poisson de taxa $\lambda$, para um registro com TTL igual a $T$. Cada erro de cache inicia um ciclo de duração $T$, durante o qual todas as consultas acertam. O número esperado de consultas por ciclo é $1 + \lambda T$, sendo a primeira o erro. Logo, a taxa de acerto será dada por
\[h = \frac{\lambda T}{1 + \lambda T}.\]Para $\lambda = 1$ consulta por segundo e $T = 300$ s, $h = 300/301 = 99{,}67\%$. A Tabela 2 mostra o comportamento em uma faixa útil.
| $\lambda$ (consultas/s) | $T = 30$ s | $T = 60$ s | $T = 300$ s | $T = 3600$ s |
|---|---|---|---|---|
| $0{,}1$ | $75{,}00\%$ | $85{,}71\%$ | $96{,}77\%$ | $99{,}72\%$ |
| $1$ | $96{,}77\%$ | $98{,}36\%$ | $99{,}67\%$ | $99{,}97\%$ |
| $10$ | $99{,}67\%$ | $99{,}83\%$ | $99{,}97\%$ | $100{,}00\%$ |
Tabela 2: taxa de acerto de cache, $h = \lambda T/(1 + \lambda T)$. Passar de 300 s para 3600 s ganha, na linha do meio, três décimos de ponto percentual e multiplica por doze a janela de inconsistência.
Leia a linha do meio da direita para a esquerda. De $3600$ para $300$ segundos, a taxa de acerto cai de $99{,}97\%$ para $99{,}67\%$, uma perda de $0{,}30$ ponto percentual. De $300$ para $60$, cai para $98{,}36\%$, perda de $1{,}31$ ponto. De $60$ para $30$, cai para $96{,}77\%$. Para um nome consultado uma vez por segundo, o retorno é fortemente decrescente acima de alguns minutos. Nesse regime, TTLs longos compram pouco desempenho adicional e ampliam muito a janela de inconsistência. A primeira linha lembra o limite da conclusão: nomes raros ainda ganham acertos relevantes quando o TTL cresce.
A propagação. Quando o valor muda, cada resolvedor continua servindo o antigo até sua cópia expirar. Se os instantes de expiração estiverem uniformemente distribuídos, o que é uma hipótese razoável para muitos resolvedores independentes, a fração ainda desatualizada no instante $t$ após a mudança será dada por
\[f(t) = 1 - \frac{t}{T}, \qquad 0 \le t \le T,\]com espera média de $T/2$ e pior caso de $T$. A Figura 2 mostra as curvas.
Figura 2: sob a hipótese de expirações uniformemente distribuídas, a convergência é linear e o pior caso é o próprio TTL. Com 3600 s, metade dos resolvedores ainda responde o valor antigo trinta minutos depois da mudança.
A conta que ninguém faz. Reduzir o TTL não é grátis, e o custo aparece no autoritativo. Se há demanda contínua pelo nome, cada resolvedor ativo consulta aproximadamente uma vez por TTL. A taxa agregada, com $N$ resolvedores independentes nesse regime, é $N/T$. Para um milhão de resolvedores:
| TTL | Consultas por segundo ao autoritativo |
|---|---|
| $3600$ s | $278$ |
| $300$ s | $3333$ |
| $30$ s | $33\,333$ |
Tabela 3: carga no autoritativo com um milhão de resolvedores continuamente ativos. Dentro desse modelo, reduzir o TTL por dez multiplica a carga por dez.
Daí sai um procedimento de migração que vale memorizar. Antes de mudar o destino, reduza o TTL e espere pelo menos o TTL antigo inteiro, para que as cópias conformes do valor antigo expirem. Só então mude o registro, agora com uma janela de cache menor. Depois de confirmar a mudança por resolvedores independentes, restaure o TTL. Quem esquece a primeira espera pode descobrir, no meio da migração, que uma fração dos resolvedores permanece presa ao valor velho por mais uma hora, com o TTL antigo que foi reduzido tarde demais. Políticas locais que impõem TTL mínimo continuam fora dessa garantia e precisam ser medidas.
3.1 Exercícios da Seção 3
1. Calcule a taxa de acerto para três TTLs
Com $\lambda = 1$ consulta por segundo, calcule a taxa de acerto de cache para TTLs de $60$, $300$ e $3600$ segundos.
Solução: Para cada TTL, vamos substituir $\lambda=1$ em $h = \lambda T/(1 + \lambda T)$:
\[h_{60} = \frac{60}{61} = 98{,}36\%, \qquad h_{300} = \frac{300}{301} = 99{,}67\%, \qquad h_{3600} = \frac{3600}{3601} = 99{,}97\%.\]Multiplicar o TTL por sessenta, de $60$ para $3600$ segundos, melhorou a taxa de acerto em $1{,}61$ ponto percentual. A aproximação $h \approx 1 - 1/(\lambda T)$, que circula bastante, dá $98{,}33\%$, $99{,}67\%$ e $99{,}97\%$, com erro no terceiro decimal; ela é boa para $\lambda T \gg 1$ e falha quando o registro é consultado raramente, caso da primeira linha da Tabela 2.
2. Determine o efeito de uma consulta rara
Um registro é consultado uma vez a cada dez segundos, isto é, $\lambda = 0{,}1$. Recalcule a taxa de acerto para os mesmos três TTLs e explique a diferença.
Solução: Agora $\lambda T$ vale $6$, $30$ e $360$:
\[h_{60} = \frac{6}{7} = 85{,}71\%, \qquad h_{300} = \frac{30}{31} = 96{,}77\%, \qquad h_{3600} = \frac{360}{361} = 99{,}72\%.\]A taxa de acerto depende do produto $\lambda T$, e não do TTL isoladamente. Com $T=60$ s, o registro raro acerta $85{,}71\%$ das consultas; com $T=3600$ s, acerta $99{,}72\%$. O aumento reduz os erros de cache de $14{,}29\%$ para $0{,}28\%$, portanto o efeito sobre consultas é grande. O preço também é grande: a janela máxima de inconsistência cresce de um minuto para uma hora. Para registros raros, precisamos calcular as duas pontas em vez de repetir a regra de que TTL longo quase não ajuda.
3. Calcule o tempo de propagação de uma mudança
Com TTL de $3600$ segundos e instantes de expiração uniformemente distribuídos, quanto tempo em média um resolvedor continua servindo o valor antigo? E no pior caso? Qual fração ainda está desatualizada após dez minutos?
Solução: A espera média é $T/2 = 1800$ s, ou $30$ minutos, e o pior caso é $T = 3600$ s, ou $60$ minutos. A fração desatualizada em $t = 600$ s será dada por
\[f(600) = 1 - \frac{600}{3600} = 0{,}8\overline{3} = 83{,}33\%.\]Dez minutos depois de uma mudança urgente, cinco em cada seis resolvedores ainda respondem o valor antigo no modelo uniforme. Se a mudança era o desvio de tráfego de um serviço quebrado, cinco sextos dos usuários atendidos por esses caches continuam sendo enviados para ele. Publicar outra resposta no autoritativo não invalida as cópias existentes: o mecanismo normal é a expiração.
| Cenário | Tempo ou fração desatualizada |
|---|---|
| Espera média | $1800$ s, ou $30$ min |
| Pior caso | $3600$ s, ou $60$ min |
| Após $600$ s | $83{,}33\%$ dos resolvedores |
Portanto, a média esconde uma cauda operacional em que alguns resolvedores conservam o valor antigo durante todo o TTL.
4. Planeje uma migração
O TTL atual é de $3600$ s e a mudança precisa propagar em até dois minutos. Descreva o procedimento e calcule os tempos.
Solução: O procedimento tem quatro etapas, e a primeira é a que se esquece.
| Etapa | Ação | Espera necessária | Por quê |
|---|---|---|---|
| 1 | reduzir o TTL para $60$ s | $3600$ s | todas as cópias com o TTL antigo precisam expirar |
| 2 | alterar o registro | nenhuma | agora a propagação é governada pelo TTL novo |
| 3 | aguardar a convergência | $60$ s | pior caso do TTL reduzido |
| 4 | restaurar o TTL para $3600$ s | nenhuma | volta a taxa de acerto e reduz a carga |
O tempo total é de $3660$ s, ou $61$ minutos, dos quais $3600$ são a espera da etapa 1. Quem pula essa espera e altera o registro logo após reduzir o TTL descobre que uma fração dos resolvedores guardou o valor antigo com o TTL antigo, e continuará servindo-o por até uma hora. É o defeito mais comum de migração de DNS, e ele só aparece para parte dos usuários, o que torna o diagnóstico penoso.
5. Calcule o custo da redução de TTL
Um domínio é consultado por um milhão de resolvedores distintos. Calcule a carga no autoritativo com TTL de $300$ s e de $30$ s. Para estimar o custo mensal, use a tabela do Route 53 consultada em 18 de agosto de 2026: US$ 0,40 por milhão nas primeiras um bilhão de consultas padrão e US$ 0,20 por milhão acima desse volume. Ignore o preço da zona hospedada.
Solução: A taxa agregada é $N/T$:
\[\frac{10^{6}}{300} = 3333\ \text{consultas/s}, \qquad \frac{10^{6}}{30} = 33\,333\ \text{consultas/s}.\]O volume mensal, com $2{,}592 \times 10^{6}$ segundos em trinta dias, será de $8{,}64 \times 10^{9}$ e $8{,}64 \times 10^{10}$ consultas. Como a tarifa muda depois do primeiro bilhão, precisamos separar os blocos:
\[C_{300} = 1000\times0{,}40 + 7640\times0{,}20 = \text{US\$ }1928,\] \[C_{30} = 1000\times0{,}40 + 85\,400\times0{,}20 = \text{US\$ }17\,480.\]| TTL | Consultas por segundo | Consultas em 30 dias | Custo estimado |
|---|---|---|---|
| $300$ s | $3333$ | $8{,}64$ bilhões | US$ $1928$ |
| $30$ s | $33\,333$ | $86{,}4$ bilhões | US$ $17\,480$ |
A carga cresce exatamente por um fator dez; a fatura cresce por $9{,}07$ porque a tarifa é progressiva por volume. O preço precisa ser verificado na data da decisão. O que permanece é a direção do compromisso: TTL menor aumenta a carga autoritativa e reduz a janela de inconsistência.
4. Latência de resolução e o custo do primeiro acesso
A resolução acontece antes de qualquer conexão, e portanto antes de tudo que o Artigo 4 calculou. Ela é, literalmente, o primeiro item da fatura.
Uma consulta fria, em que nada está em cache, percorre o caminho da Figura 1: o resolvedor recursivo pergunta à raiz, obtém a delegação para o domínio de topo, pergunta ao domínio de topo, obtém a delegação para a zona, pergunta ao autoritativo e obtém a resposta. São três idas e voltas dentro da hierarquia. Com $30$ ms por salto, valor plausível para servidores bem distribuídos:
\[T_{\text{fria}} = 3 \times 30 = 90\ \text{ms},\]aos quais se soma a ida e volta do cliente até o resolvedor recursivo, tipicamente $3$ ms para o resolvedor do provedor de acesso, totalizando $93$ ms.
Uma consulta quente, servida do cache do resolvedor, custa a ida e volta ao resolvedor: $3$ ms. Do cache do próprio sistema operacional ou do navegador, menos de $1$ ms.
O fator entre os dois extremos é de aproximadamente $93$. Ele ajuda a explicar por que a primeira visita a um nome ainda ausente dos caches pode parecer muito pior. Também explica por que a taxa de acerto da Seção 3 importa: os $99{,}67\%$ de acerto não estão economizando $0{,}33\%$ de alguma coisa, estão evitando que $0{,}33\%$ das consultas paguem, neste modelo, um custo noventa vezes maior.
Três agravantes merecem número.
Domínios múltiplos. Uma página que carrega recursos de seis domínios distintos precisa de seis resoluções. Se todas forem frias e independentes, cada uma custa os $93$ ms do modelo. Sequencialmente, $6 \times 93 = 558$ ms; em paralelo, o caminho crítico custa $93$ ms quando as seis têm a mesma duração. Navegadores sobrepõem consultas e podem antecipar nomes encontrados no documento precisamente por causa dessa conta.
Cadeias de CNAME. Cada novo nome pode exigir outra busca, mas não devemos multiplicar $90$ ms por elo sem declarar o estado do cache. A delegação e os endereços dos autoritativos consultados no primeiro elo podem servir aos seguintes. Como limite didático, suponha três buscas completas em espaços de nomes independentes, incluindo o endereço final. A hierarquia custa $3 \times 90 = 270$ ms e a ida e volta do cliente ao resolvedor é paga uma vez, totalizando $273$ ms. Na prática, precisamos medir quantas consultas externas a cadeia realmente provocou.
A escolha do resolvedor. Um resolvedor público distante, a $50$ ms, contra o resolvedor do provedor de acesso, a $3$ ms, acrescenta $47$ ms a cada resolução fria e a cada acerto de cache. Trocar de resolvedor por motivos de privacidade é uma decisão legítima e tem esse preço; o Artigo 15 volta ao assunto quando tratar de identidade e confiança.
4.1 Exercícios da Seção 4
1. Decomponha o custo de uma resolução fria
Com $30$ ms por salto na hierarquia e $3$ ms até o resolvedor recursivo, calcule o tempo de uma resolução fria e compare com uma quente servida do cache local em $1$ ms.
Solução: A hierarquia exige três consultas do resolvedor recursivo, à raiz, ao domínio de topo e ao autoritativo:
\[T_{\text{fria}} = 3 \times 30 + 3 = 93\ \text{ms}.\]A consulta quente do cache local custa $1$ ms, portanto o fator é $93$. Note que a comparação correta com a hierarquia é a do resolvedor recursivo: mesmo com o cache do resolvedor quente e o do cliente frio, o custo cai para os $3$ ms da ida e volta ao resolvedor, um fator de $31$ sobre a resolução fria completa.
| Resolução | Tempo | Fator sobre o cache local |
|---|---|---|
| Fria completa | $93$ ms | $93$ |
| Cache do resolvedor | $3$ ms | $3$ |
| Cache local | $1$ ms | $1$ |
A tabela mostra que cada nível de cache elimina uma parte distinta da hierarquia de consultas.
2. Calcule o custo de uma página com seis domínios
Uma página carrega recursos de seis domínios distintos, todos frios e independentes. Cada resolução custa $90$ ms dentro da hierarquia e $3$ ms entre cliente e resolvedor. Compare o custo sequencial e o paralelo.
Solução: Cada resolução completa custa
\[T_1 = 3\times30 + 3 = 93\ \text{ms}.\]Sequencialmente, as seis resoluções somam $6 \times 93 = 558$ ms. Em paralelo, todas partem juntas e o custo é o da mais lenta, ou $93$ ms sob a hipótese de tempos iguais.
A economia no caminho crítico é de $558-93=465$ ms. Registre, porém, a composição de caudas da Seção 6 do Artigo 1: se cada resolução tem probabilidade $p$ de exceder um limiar, a probabilidade de ao menos uma das seis exceder é $1 - (1-p)^{6}$. Para $p = 0{,}05$, isso dá $26{,}5\%$. O paralelismo reduz a soma para o máximo; o fan-out continua expondo a página à cauda da mais lenta.
| Estratégia | Tempo total | Economia |
|---|---|---|
| Sequencial | $558$ ms | $0$ ms |
| Paralela | $93$ ms | $465$ ms |
Portanto, o paralelismo reduz o caminho crítico por um fator seis sob tempos iguais, embora a página ainda herde a cauda da resolução mais lenta.
3. Quantifique o custo de uma cadeia de CNAME
Uma resolução exige três buscas frias completas em espaços de nomes independentes, incluindo a busca do A final. Use esse caso como limite superior para uma cadeia de CNAME, calcule o custo e proponha a correção.
Solução: Cada busca externa custa $90$ ms dentro da hierarquia. A consulta do cliente ao resolvedor envolve uma ida e volta de $3$ ms para a operação completa. Logo, o limite será
\[3 \times 90 + 3 = 273\ \text{ms},\]contra $93$ ms de um A direto no mesmo modelo, um acréscimo de $180$ ms. Na prática, elos sucessivos podem reutilizar delegações, endereços de autoritativos e até respostas incluídas na mesma mensagem. Por isso, $273$ ms é um limite construído pelas hipóteses, não uma tarifa fixa por três nomes.
A correção depende de onde a cadeia está. No ápice da zona, a saída é o ALIAS do provedor, que resolve a cadeia do lado do autoritativo e devolve um endereço direto. Fora do ápice, a saída é encurtar a cadeia, apontando diretamente para o alvo final sempre que o provedor intermediário não for necessário.
4. Compare dois resolvedores
Um resolvedor público está a $50$ ms e o do provedor de acesso a $3$ ms. Calcule o efeito sobre uma resolução quente e sobre uma fria, e sobre uma página com seis domínios já em cache.
Solução: Na resolução quente, o custo é a ida e volta ao resolvedor: $50$ ms contra $3$ ms, uma diferença de $47$ ms. Na fria, ambos pagam os $90$ ms da hierarquia, e a diferença continua sendo os mesmos $47$ ms, agora sobre uma base maior: $140$ ms contra $93$ ms, ou $50{,}5\%$ a mais.
Para a página com seis domínios em cache, resolvidos em paralelo, a diferença permanece $47$ ms, porque o paralelismo faz o custo ser o do mais lento e todos estão à mesma distância. O resultado é contraintuitivo e vale registrar: a penalidade de um resolvedor distante é paga uma vez por página, não uma vez por domínio, desde que a resolução seja paralela.
| Situação | Provedor a $3$ ms | Público a $50$ ms | Penalidade |
|---|---|---|---|
| Resolução quente | $3$ ms | $50$ ms | $47$ ms |
| Resolução fria | $93$ ms | $140$ ms | $47$ ms |
| Seis domínios quentes em paralelo | $3$ ms | $50$ ms | $47$ ms |
Assim, a distância acrescenta $47$ ms ao caminho crítico, e não $47$ ms por domínio quando as consultas são paralelas.
5. Determine o efeito do cache do sistema operacional
O sistema operacional mantém um cache próprio com TTL respeitado. Um serviço consulta o mesmo nome dez vezes por segundo, com TTL de $30$ s. Calcule as consultas que efetivamente saem da máquina.
Solução: Pela fórmula da Seção 3, com $\lambda = 10$ e $T = 30$, temos $\lambda T = 300$ e $h = 300/301 = 99{,}67\%$. Das $10$ consultas por segundo, saem da máquina
\[10 \times (1 - 0{,}9967) = 0{,}033\ \text{consulta/s},\]ou uma a cada $30$ segundos, que é exatamente uma por TTL. O cache local transforma dez consultas por segundo em uma consulta a cada meio minuto.
A consequência tem dois lados. De um lado, a rede é poupada. De outro, o processo fica preso ao valor antigo durante todo o TTL, e um serviço que caia será procurado no endereço errado por até 30 segundos, mesmo que o DNS já esteja publicando o endereço correto. É a mesma janela de inconsistência da Seção 3, agora dentro do processo, e é a razão de a Seção 6 somar o TTL ao tempo de detecção.
5. Políticas de roteamento como decisão de engenharia
Até aqui, um nome resolveu para um endereço. Provedores de nuvem transformaram o DNS em ponto de decisão: a resposta passa a depender de quem pergunta, de onde pergunta e do estado do sistema. É o roteamento por DNS, e ele é a forma mais barata de distribuição global de tráfego que existe, com limitações que precisam ser ditas.
Simples. Um conjunto de registros, todos devolvidos, e o cliente escolhe. Sem inteligência nenhuma. Serve como linha de base.
Ponderada. Cada registro recebe um peso, e o autoritativo escolhe respostas nessa proporção. A unidade da decisão é a consulta que chega ao autoritativo, não a requisição da aplicação. Com pesos $70/30$ e mil consultas autoritativas independentes, o número esperado de respostas para o primeiro alvo é $700$, com desvio padrão
\[\sigma = \sqrt{n p (1-p)} = \sqrt{1000 \times 0{,}7 \times 0{,}3} = 14{,}49,\]de modo que aproximadamente $95\%$ das amostras caem entre $671$ e $729$ pela aproximação normal. Medir $680$ respostas para o primeiro alvo é compatível com a política. Isso não permite afirmar que $68\%$ das requisições chegaram a ele, porque cada resposta pode ficar em cache e representar populações de tamanhos muito diferentes.
A Figura 3 mostra o menor contraexemplo útil. Quatro resolvedores recebem duas respostas para A e duas para B, exatamente $50/50$. Como o resolvedor que recebeu A atende muito mais requisições, o tráfego observado termina em $77{,}1/22{,}9$. O DNS acertou os pesos das respostas e não controlou a fração de requisições.
Figura 3: pesos governam respostas autoritativas. O cache e o tamanho desigual das populações transformam essas respostas em outra distribuição de requisições.
Por latência. O autoritativo estima qual região responde mais rápido para quem perguntou e devolve o endereço daquela região. Aqui está a limitação que precisa ser dita com todas as letras: o autoritativo não vê o cliente, vê o resolvedor recursivo. Um usuário em Manaus usando um resolvedor público cujo ponto de presença está em São Paulo será tratado como se estivesse em São Paulo. A extensão de sub-rede do cliente, da RFC 7871, permite ao resolvedor repassar um prefixo do endereço do cliente e existe justamente para corrigir isso; ela não é universal, e tem custo de privacidade e de fragmentação de cache, porque a resposta passa a depender do prefixo e não apenas do nome.
Por geolocalização. Decide por país ou continente, e serve a requisitos regulatórios e de conteúdo, não a desempenho. Sofre da mesma limitação de enxergar o resolvedor.
Por falha. Um registro primário e um secundário, com verificação de saúde. O secundário só é devolvido quando o primário é considerado indisponível. É o assunto da Seção 6.
Multivalor. Vários registros saudáveis devolvidos ao mesmo tempo, deixando o cliente escolher e reagir. Combina verificação de saúde com a distribuição do cliente.
Uma observação sobre disponibilidade que vale para todas as políticas de falha. Com dois destinos independentes de disponibilidade $a = 99{,}9\%$ cada, a disponibilidade combinada será dada por
\[A = 1 - (1-a)^{2} = 1 - 10^{-6} = 99{,}9999\%,\]o que reduz a indisponibilidade anual de $525{,}6$ para $0{,}53$ minuto. O número é lindo e a hipótese é otimista. Duas regiões do mesmo provedor compartilham plano de controle, processos de implantação, versões de software e, frequentemente, equipe de plantão. A correlação entre as falhas é positiva, e a disponibilidade real fica em algum ponto entre $99{,}9\%$ e $99{,}9999\%$ que nenhuma fórmula fornece. O Artigo 24, na trilha complementar, retoma esse cálculo com o cuidado que ele merece.
5.1 Exercícios da Seção 5
1. Avalie uma distribuição ponderada
Uma política ponderada $70/30$ recebe mil consultas e o alvo primário recebe $680$. A política está funcionando?
Solução: Se as mil observações são consultas que chegaram ao autoritativo e cada escolha é independente, cada uma funciona como um ensaio de Bernoulli com $p = 0{,}7$. O número de respostas para o primário é binomial com média $np = 700$ e desvio padrão
\[\sigma = \sqrt{1000 \times 0{,}7 \times 0{,}3} = \sqrt{210} = 14{,}49.\]O valor observado está a $(700 - 680)/14{,}49 = 1{,}38$ desvio da média, dentro do intervalo aproximado de $95\%$, que vai de $671$ a $729$. A amostra é compatível com a política; ela não prova que a implementação está correta e não mede a fração de requisições.
Para verificar o efeito sobre tráfego, precisamos de duas contagens diferentes:
| Camada medida | Contagem necessária | O que ela permite concluir |
|---|---|---|
| autoritativo DNS | respostas A e B | aderência dos sorteios aos pesos |
| destinos da aplicação | requisições recebidas por A e B | exposição real de usuários e carga |
Confundir as duas camadas produz um canário que parece estar em $5\%$ na configuração e pode estar muito acima ou abaixo disso na aplicação.
2. Dimensione uma implantação canário
Uma implantação usa peso DNS de $5\%$. Cem resolvedores guardam as respostas. Cinco resolvedores corporativos representam $100\,000$ requisições diárias cada; os outros noventa e cinco representam $100$ requisições cada. Suponha que exatamente cinco respostas apontem para o canário. Compare dois casos: no primeiro, uma resposta pertence a um resolvedor corporativo e quatro a resolvedores pequenos; no segundo, as cinco pertencem a resolvedores pequenos.
Solução: O tráfego diário total será
\[5\times100\,000 + 95\times100 = 509\,500\ \text{requisições}.\]No primeiro caso, o canário recebe um resolvedor corporativo e quatro pequenos:
\[\frac{100\,000 + 4\times100}{509\,500} = 19{,}71\%.\]No segundo, recebe apenas cinco populações pequenas:
\[\frac{5\times100}{509\,500} = 0{,}098\%.\]| Caso | Respostas DNS para o canário | Requisições no canário | Fração real |
|---|---|---|---|
| um resolvedor corporativo e quatro pequenos | $5\%$ | $100\,400$ | $19{,}71\%$ |
| cinco resolvedores pequenos | $5\%$ | $500$ | $0{,}098\%$ |
O mesmo peso nominal produz exposições que diferem por um fator superior a duzentos. Se precisamos controlar porcentagem de requisições, o ponto de decisão deve enxergar requisições, como um balanceador ou a própria aplicação. O DNS ponderado continua útil para migração grossa entre populações, desde que a exposição real seja medida nos destinos.
3. Mostre a limitação do roteamento por latência
Um usuário em Manaus usa um resolvedor público cujo ponto de presença mais próximo está em São Paulo. Que região a política por latência escolherá e por quê?
Solução: O servidor autoritativo recebe a consulta do resolvedor, não do usuário, e a única informação de origem disponível é o endereço do resolvedor. Ele mede, portanto, a latência entre São Paulo e as regiões candidatas, e devolve a região melhor para São Paulo.
Se a melhor região para São Paulo for a Virgínia, o usuário de Manaus será enviado à Virgínia, mesmo que exista uma região mais próxima dele. O erro pode chegar a milhares de quilômetros e, pelos números do Artigo 1, a dezenas de milissegundos de RTT desnecessário.
A correção parcial é a extensão de sub-rede do cliente da RFC 7871, na qual o resolvedor repassa os primeiros bits do endereço do usuário. Ela custa privacidade, porque expõe a origem aproximada de cada consulta ao autoritativo, e custa eficiência de cache, porque a resposta passa a variar por prefixo, reduzindo o $\lambda$ efetivo de cada entrada e, pela fórmula da Seção 3, a taxa de acerto.
4. Calcule a disponibilidade de uma configuração com dois destinos
Dois destinos com disponibilidade individual de $99{,}9\%$, com falha independente. Calcule a disponibilidade combinada e a indisponibilidade anual.
Solução: O serviço só cai se ambos caírem:
\[A = 1 - (1 - 0{,}999)^{2} = 1 - 10^{-6} = 99{,}9999\%.\]A indisponibilidade anual passa de $0{,}001 \times 525\,600 = 525{,}6$ minutos para $10^{-6} \times 525\,600 = 0{,}53$ minuto.
A hipótese de independência é o ponto frágil. Se as falhas forem perfeitamente correlacionadas, a disponibilidade combinada continua $99{,}9\%$ e os $525{,}6$ minutos permanecem. O valor real depende da fração de causas comuns, e a única forma de reduzi-la é remover compartilhamento: provedores distintos, regiões distintas, processos de implantação decalados no tempo. Registre que redundância sem independência é decoração, e que a fórmula não informa se a independência existe: ela apenas supõe.
5. Determine quando a política por falha não protege
Um serviço usa política por falha entre duas regiões, ambas atrás do mesmo servidor autoritativo, hospedado em um único provedor de DNS. O que a política protege e o que ela não protege?
Solução: A política protege contra a falha de uma das duas regiões: a verificação de saúde detecta, o autoritativo deixa de devolver o endereço da região caída e o tráfego migra, no tempo que a Seção 6 calcula.
Ela não protege contra a falha do próprio serviço de DNS. Se o autoritativo ficar inalcançável, nenhuma resposta é produzida, e a política de falha não tem como agir porque ela vive exatamente ali. Enquanto os TTLs em cache não expirarem, os clientes continuam funcionando com o valor que já têm; depois disso, o nome deixa de resolver e as duas regiões saudáveis ficam inalcançáveis por um problema que não é delas.
A defesa é delegar a zona a conjuntos de servidores de provedores distintos, com os registros NS do ápice apontando para ambos, de modo que a indisponibilidade de um provedor não impeça a resolução. É exatamente a lição que o incidente da Seção 7 ensinou à indústria, ao custo de várias horas de Internet quebrada.
6. Verificação de saúde e o tempo real de recuperação
Esta é a seção que muda decisões, e ela existe porque o tempo de recuperação de um serviço com DNS quase nunca é o que a equipe imagina.
O mecanismo tem três parâmetros. O intervalo é o período entre sondagens; o limiar é o número de sondagens falhas consecutivas necessárias para declarar o alvo indisponível; e o TTL é o do registro que a política devolve. Se a falha ocorre logo depois de uma sondagem bem-sucedida e um resolvedor acabou de guardar a resposta antiga, um limite superior didático será
\[T_{\text{rec}} = \text{intervalo} \times \text{limiar} + \text{TTL}.\]A primeira parcela limita a detecção nesse cenário; a segunda limita a propagação para um cache que respeita o TTL. O valor real depende da fase da sondagem e da idade de cada entrada. A Tabela 4 mantém o pior caso construído para mostrar o peso de cada parâmetro.
| Intervalo | Limiar | TTL | Detecção | Total |
|---|---|---|---|---|
| $30$ s | 3 | $60$ s | $90$ s | $150$ s |
| $30$ s | 3 | $300$ s | $90$ s | $390$ s |
| $10$ s | 3 | $60$ s | $30$ s | $90$ s |
| $10$ s | 2 | $20$ s | $20$ s | $40$ s |
| $5$ s | 2 | $30$ s | $10$ s | $40$ s |
Tabela 4: tempo até o tráfego migrar, no pior caso. Entre a primeira e a segunda linha, apenas o TTL mudou, e o tempo de recuperação subiu 160%.
Compare as duas primeiras linhas. A detecção é idêntica, de $90$ segundos, e o total salta de $150$ para $390$ segundos porque o TTL passou de $60$ para $300$. Na segunda configuração, o TTL responde por $77\%$ do limite. Uma equipe que ajuste intervalo e limiar sem examinar esse valor pode reduzir a detecção à metade e ainda preservar a maior parcela do tempo de recuperação.
A Figura 4 empilha as duas parcelas para as cinco configurações.
Figura 4: no pior caso construído, a detecção e o TTL somam-se. Nas duas primeiras configurações, apenas o TTL mudou, e ele responde por 40% e por 77% do limite.
Há três razões para não levar todos os parâmetros ao mínimo, e as três precisam ser quantificadas.
Custo no autoritativo. Reduzir o TTL multiplica as consultas, pela Tabela 3. O TTL de $20$ segundos da quarta linha custa quinze vezes mais consultas que o de $300$.
Custo da sondagem. Sondar de cinco em cinco segundos a partir de várias localidades produz um volume de requisições que o próprio serviço precisa atender, e que aparece nos registros de acesso como tráfego que não é de usuário.
Falso positivo. Com limiar $2$, intervalo de $10$ s e probabilidade independente de erro transitório de $1\%$ por sondagem, qualquer par consecutivo falha com probabilidade $10^{-4}$. Tratar os $8640$ instantes do dia como oportunidades produz aproximadamente $0{,}86$ sequência por dia, ou uma a cada $28$ horas. É uma aproximação, porque pares vizinhos se sobrepõem e erros reais podem ser correlacionados. O limiar troca tempo de detecção por robustez, e o valor certo depende da taxa e da correlação observadas.
Falta distinguir duas situações que costumam ser somadas indevidamente. Quando cada camada respeita o TTL remanescente devolvido pela camada anterior, três caches não criam três TTLs completos. Todos permanecem dentro da janela iniciada no cache mais antigo. O tempo pode exceder o TTL autoritativo quando uma camada impõe sua própria política, por exemplo ao fixar sessenta segundos mesmo que tenha recebido um segundo remanescente. Nesse caso, o excesso pertence à política local e precisa ser medido como tal.
6.1 Exercícios da Seção 6
1. Calcule o tempo de recuperação de duas configurações
Com intervalo de $30$ s e limiar $3$, calcule o tempo de recuperação para TTL de $60$ s e de $300$ s.
Solução: A detecção é $30 \times 3 = 90$ s nas duas. Somando o TTL:
\[T_{60} = 90 + 60 = 150\ \text{s}, \qquad T_{300} = 90 + 300 = 390\ \text{s}.\]A diferença é de $240$ s, ou quatro minutos, e vem inteiramente de um parâmetro que não tem relação nenhuma com a verificação de saúde. No primeiro caso o TTL responde por $40\%$ do total; no segundo, por $76{,}9\%$. Antes de investir em sondagem mais frequente, vale conferir qual das duas parcelas domina.
2. Projete uma configuração para um alvo de 60 segundos
Determine uma configuração cujo tempo de recuperação fique abaixo de $60$ s, e calcule o custo em consultas ao autoritativo com um milhão de resolvedores.
Solução: É preciso que $\text{intervalo} \times \text{limiar} + \text{TTL} < 60$. Com intervalo de $10$ s e limiar $2$, a detecção é $20$ s e sobram $40$ s para o TTL; adotando $30$ s, o total será dado por
\[T_{\text{rec}} = 20 + 30 = 50\ \text{s}.\]O custo em consultas, pela Tabela 3, é $10^{6}/30 = 33\,333$ consultas por segundo, contra $3333$ com TTL de $300$ s. Dez vezes mais consultas para reduzir a recuperação de $390$ para $50$ segundos.
A decisão é econômica e não técnica: vale saber quanto custa um minuto de indisponibilidade para o negócio e comparar com a diferença de fatura. Uma conta é substituída pela outra, o que já é melhor que substituí-la por intuição.
3. Calcule a taxa de falso positivo
Com intervalo de $10$ s, limiar $2$ e probabilidade de erro transitório de $1\%$ por sondagem, calcule a frequência esperada de remoções indevidas.
Solução: Duas falhas consecutivas independentes têm probabilidade
\[p = 0{,}01^{2} = 10^{-4}\]por ciclo de avaliação. Com $86\,400/10 = 8640$ ciclos por dia, o número esperado será dado por
\[8640 \times 10^{-4} = 0{,}864\ \text{remoção indevida por dia},\]ou uma a cada $27{,}8$ horas. Elevar o limiar para $3$ reduz para $8640 \times 10^{-6} = 0{,}0086$ por dia, ou uma a cada $116$ dias, ao custo de $10$ s a mais de detecção. A frequência cai por um fator cem, isto é, duas ordens de grandeza, sob a hipótese de independência.
4. Separe TTL remanescente de política local
Use a configuração do exercício 1, com detecção de $90$ s e TTL autoritativo de $60$ s. Compare dois clientes. O primeiro respeita o TTL remanescente em todas as camadas. O segundo usa um navegador que fixa sua entrada por $60$ s, mesmo quando recebe apenas $1$ s de TTL remanescente no instante $59$ após a mudança do registro.
Solução: No cliente compatível, o resolvedor recursivo pode conservar a resposta antiga por até $60$ s depois da mudança. Se o sistema operacional consulta quando restam $10$ s, recebe esse valor remanescente; se o navegador consulta um pouco depois, recebe um valor ainda menor. As camadas não reiniciam o relógio. O limite permanece
\[90 + 60 = 150\ \text{s}.\]No segundo cliente, o navegador consulta no instante $59$ e recebe uma resposta antiga com $1$ s remanescente. Ao substituí-lo por uma retenção local fixa de $60$ s, ele pode servir o endereço antigo até o instante $119$ depois da mudança. O limite percebido passa a
\[90 + 119 = 209\ \text{s}.\]| Cliente | Regra das camadas | Limite após a detecção |
|---|---|---|
| compatível | preserva o TTL remanescente | $60$ s |
| navegador com retenção fixa | transforma $1$ s remanescente em $60$ s | $119$ s |
O valor adicional não vem da existência de três caches. Vem de uma camada que estende deliberadamente o TTL recebido. Para diagnosticar esse caso, precisamos registrar o TTL devolvido em cada consulta e a política efetiva do cliente.
5. Explique por que a verificação de saúde pode enganar
Uma verificação de saúde consulta a rota /health de um serviço, que devolve $200$ sempre que o processo está de pé. Que classe de falha ela não detecta?
Solução: Ela detecta o processo morto, o porto fechado e a máquina inalcançável. Não detecta nenhuma falha em que o processo está vivo e o serviço está quebrado: banco de dados inacessível, dependência externa fora do ar, disco cheio, coletor de lixo em pausa prolongada, ou a versão nova respondendo $200$ com conteúdo errado.
Nesses casos a verificação continua verde, o destino permanece no conjunto e o tempo de recuperação é infinito, porque a migração nunca é acionada. A correção é fazer a rota de saúde exercitar as dependências críticas e devolver falha quando alguma delas estiver indisponível, aceitando o risco oposto: uma dependência lenta passa a derrubar destinos saudáveis, e a falha se propaga em vez de ficar contida.
Não há escolha universalmente certa entre as duas, e é por isso que a prática comum separa duas rotas, uma de vivacidade, que só diz se o processo respira, e outra de prontidão, que diz se ele consegue atender. O Artigo 14 volta a essa distinção com os nomes que o Kubernetes lhes dá.
7. O DNS como superfície de ataque e de falha
As especificações originais do DNS não ofereciam autenticação criptográfica nem confidencialidade por padrão. Três consequências dessa decisão ainda estão conosco.
Amplificação. Uma consulta UDP pequena pode produzir uma resposta grande, e o UDP não confirma a origem antes de responder. Um atacante envia consultas com endereço de origem forjado, o da vítima, e o servidor devolve as respostas para ela. Em um exemplo com consulta de $64$ bytes e resposta de $3000$ bytes, o fator de tamanho será dado por
\[\frac{3000}{64} = 46{,}9,\]o que estabelece um teto aritmético de $46{,}9$ para a amplificação naquele formato. O tráfego efetivo também é limitado pela capacidade e pela política dos servidores usados como refletores. As defesas envolvem limitar a taxa de resposta, recusar recursão a quem não é cliente e, na camada de rede, filtrar pacotes com origem forjada na saída, prática que o Artigo 6 vai retomar.
Envenenamento de cache. Um resolvedor que aceite uma resposta forjada guarda o valor errado por todo o TTL, e o serve a todos os seus clientes. A defesa estrutural é o DNSSEC, que assina os registros criptograficamente e permite ao resolvedor validar a cadeia desde a raiz. Sua adoção é parcial e sua operação é delicada: uma chave expirada torna a zona inválida, e portanto inalcançável para resolvedores validadores, o que já produziu indisponibilidades notáveis. Zero validação é inseguro; validação mal operada é indisponível.
Privacidade. Consultas em texto claro expõem, a qualquer intermediário, todos os nomes que a leitora acessa. As respostas são DoT e DoH, que cifram o transporte entre cliente e resolvedor. Elas não cifram nada entre o resolvedor e os autoritativos, e transferem a visibilidade do provedor de acesso para o operador do resolvedor. É uma mudança de quem confia em quem, e não uma eliminação da confiança.
Vale examinar dois incidentes, porque ambos ensinaram coisas que estão nas seções anteriores.
Dyn, outubro de 2016. Um ataque distribuído de negação de serviço, originado de uma rede de dispositivos comprometidos, atingiu a infraestrutura autoritativa da Dyn em ondas ao longo de um dia. Serviços que dependiam daquele provedor ficaram inalcançáveis, ainda que seus próprios servidores estivessem intactos. O incidente tornou concreta a pergunta do exercício 5 da Seção 5: o plano de redundância inclui o serviço autoritativo ou termina antes dele?
Outubro de 2021. Uma alteração de configuração retirou os anúncios de rota dos prefixos que continham os servidores autoritativos de uma grande empresa. Os servidores estavam funcionando; simplesmente deixaram de ser alcançáveis. Sem autoritativo, os nomes deixaram de resolver quando os caches expiraram, e todos os serviços da empresa desapareceram da Internet por cerca de seis horas. O detalhe que interessa à engenharia é que a falha original era de roteamento, tema do próximo artigo, e se manifestou como falha de nomes, porque a dependência entre as duas camadas é assimétrica e silenciosa.
Uma lição operacional que os dois casos compartilham: durante o intervalo em que os caches ainda tinham validade, parte dos usuários continuou funcionando. O cache que estende uma queda é o mesmo que a atrasa. É o compromisso da Seção 3 aparecendo em um dia ruim, e é a razão de haver quem defenda TTLs mais longos precisamente por resiliência, aceitando a rigidez em troca.
8. Anycast e a raiz distribuída
Há uma pergunta que a Seção 1 deixou pendente. Se existem apenas treze identificadores de servidores raiz, e a Internet inteira depende deles, como isso não é um gargalo absurdo?
A resposta é o anycast. Um mesmo endereço é anunciado, por roteamento, a partir de dezenas ou centenas de locais físicos. O roteamento entrega o pacote segundo a topologia, e nem o cliente nem o servidor precisam saber que existem outros locais. Em 5 de agosto de 2026, os treze identificadores correspondiam a $2002$ instâncias operacionais espalhadas pelo planeta.
O ganho de latência é direto. Suponha usuários distribuídos como na tabela abaixo, com a latência até um servidor único em São Paulo e até a instância anycast mais próxima de cada um:
| Região do usuário | Fração | Até o nó único | Até a instância mais próxima |
|---|---|---|---|
| A | $40\%$ | $95$ ms | $8$ ms |
| B | $25\%$ | $95$ ms | $95$ ms |
| C | $20\%$ | $95$ ms | $120$ ms |
| D | $10\%$ | $95$ ms | $180$ ms |
| E | $5\%$ | $95$ ms | $210$ ms |
A latência média com o nó único é $95$ ms. Com anycast, é a média ponderada da última coluna:
\[0{,}40 \times 8 + 0{,}25 \times 95 + 0{,}20 \times 120 + 0{,}10 \times 180 + 0{,}05 \times 210 = 79{,}45\ \text{ms},\]uma redução de $16{,}4\%$. Repare que algumas regiões pioraram: a coluna da direita é maior que a do meio nas linhas C, D e E, porque a instância topologicamente mais próxima nem sempre é a geograficamente mais próxima. Anycast otimiza a média e não garante o percentil de ninguém, o que é uma propriedade importante e raramente mencionada.
Há ainda dois pontos que a leitora precisa levar.
Anycast e UDP combinam; anycast e TCP exigem cuidado. Cada consulta UDP é independente, e se duas consultas consecutivas forem para instâncias diferentes, nada se perde. Uma conexão TCP tem estado em uma instância; se a rota mudar no meio, os pacotes chegam a uma instância que não conhece aquela conexão e ela morre. Como o DNS usa TCP para respostas grandes e para transferência de zona, operadores anycast tomam cuidado com a estabilidade das rotas, e é uma das razões de o QUIC, com sua migração por identificador da Seção 8 do Artigo 4, ser interessante também aqui.
Anycast pode reagir sem esperar o TTL. Se uma instância cai e o anúncio do prefixo é retirado, o roteamento procura outro caminho. O tempo depende da detecção, da retirada e da convergência, portanto pode ir de segundos a minutos. A propriedade importante é outra: o endereço não muda e nenhum cache DNS precisa expirar. As duas técnicas podem trabalhar juntas, com anycast reagindo dentro de um serviço e DNS decidindo entre serviços.
8.1 Exercícios da Seção 8
1. Calcule o ganho de latência do anycast
Com a distribuição de usuários da tabela desta seção, calcule a latência média com o nó único e com anycast.
Solução: Com nó único, todos pagam $95$ ms, e a média é $95$ ms. Com anycast, a média ponderada será dada por
\[0{,}40(8) + 0{,}25(95) + 0{,}20(120) + 0{,}10(180) + 0{,}05(210) = 3{,}2 + 23{,}75 + 24 + 18 + 10{,}5 = 79{,}45\ \text{ms}.\]A redução é de $15{,}55$ ms, ou $16{,}4\%$. Note que o ganho vem quase inteiramente da região A, responsável por $40\%$ dos usuários e por uma queda de $87$ ms; as regiões C, D e E pioraram. Anycast é uma otimização de média cujo benefício se concentra onde há instâncias, e é por isso que a distribuição geográfica dos usuários precisa entrar na decisão de onde instalar pontos de presença.
2. Explique por que uma sessão TCP pode quebrar sobre anycast
Uma transferência de zona por TCP atravessa uma mudança de rota. Descreva o que acontece.
Solução: A conexão foi estabelecida com uma instância específica, que mantém números de sequência, janelas e o estado da transferência. A mudança de rota faz os pacotes seguintes chegarem a outra instância, que anuncia o mesmo endereço e não tem estado nenhum daquela conexão. Ela responde com um segmento de reinicialização, e a conexão morre.
O cliente precisa reabrir e recomeçar. Para uma consulta UDP isso seria invisível; para uma transferência de zona de vários megabytes, significa perder o progresso. Daí a exigência de estabilidade de rota nos serviços anycast que usam TCP, e daí também o interesse por transportes cuja identidade de conexão não dependa do endereço, como o QUIC do artigo anterior.
3. Compare os tempos de reação de anycast e de DNS
Uma instância cai. Suponha que a retirada do anúncio e a convergência anycast levem $15$ s. Compare esse valor com as configurações DNS de $150$ s e $390$ s da Tabela 4.
Solução: Pela hipótese do enunciado, o caminho anycast migra em $15$ s. A decisão acontece no roteamento e o endereço não muda, portanto não acrescentamos TTL DNS.
Pelo DNS, os dois casos pedidos custam $150$ s e $390$ s no pior caso construído da Seção 6.
As razões são $150/15=10$ e $390/15=26$. Neste exemplo, anycast reage entre dez e vinte e seis vezes mais depressa. A conclusão depende dos $15$ s fornecidos; não existe tempo universal de convergência anycast.
| Mecanismo | Tempo de reação | Limite dominante |
|---|---|---|
| Anycast, hipótese do exercício | $15$ s | retirada e convergência de roteamento |
| DNS, duas configurações | $150$ s e $390$ s | detecção e TTL |
Portanto, o anycast reage mais depressa sob os parâmetros do exercício, enquanto o DNS oferece uma política mais fina entre serviços.
4. Determine o efeito da dispersão sobre um ataque volumétrico
Um ataque de $600$ Gbit/s é dirigido a um endereço anycast anunciado de $200$ locais. Estime o volume por local e comente.
Solução: O tráfego de ataque também é roteado ao local topologicamente mais próximo de cada origem. Se as origens estiverem uniformemente distribuídas, cada local recebe em média
\[\frac{600}{200} = 3\ \text{Gbit/s},\]mas essa média não diz se algum local sobrevive. Um ponto com capacidade de $2$ Gbit/s já estaria saturado; um ponto com $10$ Gbit/s teria margem. Anycast dispersa origens segundo a topologia, mas não cria capacidade e não garante divisão uniforme.
A hipótese de uniformidade é o ponto a vigiar. Redes de dispositivos comprometidos são concentradas geograficamente, e o local mais próximo da concentração pode receber uma fração muito maior que $1/200$. A defesa dimensiona-se pela concentração observada e não pela média, aplicando o mesmo raciocínio de percentil contra média da Seção 6 do Artigo 1.
5. Determine quando anycast não resolve
Um serviço mantém estado por usuário na instância que o atende. Anycast serve?
Solução: Anycast, sozinho, não resolve. Consultas sucessivas do mesmo usuário podem chegar a instâncias diferentes conforme a rota mude, e cada instância só conhece o estado que ela própria criou. O usuário observa um serviço que esquece o que acabou de fazer.
As saídas são três, e todas custam. Manter o estado em um repositório compartilhado, pagando a latência de acesso a ele em cada requisição; usar afinidade de sessão, o que exige que a camada de decisão veja o usuário e não a rota, e portanto contraria a premissa do anycast; ou tornar o serviço sem estado, transportando o estado no próprio pedido, que é o que a Seção 4 do Artigo 15 vai chamar de decisão local com dado assinado.
9. O núcleo de um resolvedor em C++23
O formato de mensagem do DNS é pequeno e tem uma particularidade que vale implementar à mão: a compressão de nomes. Como o mesmo sufixo aparece muitas vezes em uma resposta, a RFC 1035 permite substituir um sufixo por um ponteiro de catorze bits para outro ponto da mesma mensagem. É engenhoso, economiza espaço real, e abre uma porta de segurança que precisa ser fechada explicitamente.
Um rótulo cujos dois bits mais significativos são 11 é um ponteiro; se são 00, é um rótulo comum, com o comprimento nos seis bits restantes. Nada no formato impede que um ponteiro aponte para si mesmo, ou que dois ponteiros apontem um para o outro. Um analisador ingênuo entra em laço infinito, e uma mensagem de dezoito bytes derruba o processo. É a bomba de descompressão, e a defesa é um limite de saltos.
O programa abaixo constrói uma consulta, analisa uma resposta sintética completa com dois níveis de compressão e recusa uma mensagem com ponteiro circular. Ele não envia datagramas e, portanto, não é um resolvedor completo; implementa o núcleo de codificação e análise que um resolvedor precisaria proteger. O código usa std::expected para propagar erro sem exceções nem códigos mágicos, std::span para não copiar o buffer e std::print com std::format para a saída.
// dns.cpp -- construcao de consulta e analise de resposta DNS, com compressao de nomes.
// MSVC 19.51: cl /std:c++latest /W4 /WX /EHsc /O2 dns.cpp
// Clang: clang++ -std=c++23 -stdlib=libc++ -O2 -Wall -Wextra dns.cpp -o dns
#include <cstddef>
#include <cstdint>
#include <expected>
#include <format>
#include <print>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace dns {
enum class Erro {
mensagem_curta, // faltam bytes para o campo pedido
rotulo_invalido, // os dois bits altos nao sao 00 nem 11
rotulo_longo, // um rotulo DNS passa de 63 bytes
ponteiro_fora, // o ponteiro aponta para fora da mensagem
laco_de_ponteiros, // cadeia de ponteiros longa demais
nome_longo, // o nome montado passa de 255 bytes
rdata_invalida // o valor de um registro invade o campo seguinte
};
constexpr std::string_view descrever(Erro e) {
switch (e) {
case Erro::mensagem_curta: return "mensagem curta";
case Erro::rotulo_invalido: return "rotulo invalido";
case Erro::rotulo_longo: return "rotulo longo demais";
case Erro::ponteiro_fora: return "ponteiro fora da mensagem";
case Erro::laco_de_ponteiros: return "laco de ponteiros";
case Erro::nome_longo: return "nome longo demais";
case Erro::rdata_invalida: return "rdata invalida";
}
return "erro desconhecido";
}
// ---------- construcao ----------
// Codifica "www.example.com" como 3www7example3com0, o formato de rotulos da RFC 1035.
std::expected<std::vector<std::byte>, Erro> codificar_nome(std::string_view nome) {
if (nome == ".") return std::vector<std::byte>{std::byte{0}};
if (nome.ends_with('.')) nome.remove_suffix(1);
if (nome.empty()) return std::unexpected(Erro::rotulo_invalido);
std::vector<std::byte> saida;
saida.reserve(nome.size() + 2);
std::size_t inicio = 0;
while (inicio < nome.size()) {
const auto ponto = nome.find('.', inicio);
const auto fim = (ponto == std::string_view::npos) ? nome.size() : ponto;
const auto tam = fim - inicio;
if (tam == 0) return std::unexpected(Erro::rotulo_invalido);
if (tam > 63) return std::unexpected(Erro::rotulo_longo);
if (saida.size() + 1 + tam + 1 > 255) return std::unexpected(Erro::nome_longo);
saida.push_back(static_cast<std::byte>(tam));
for (std::size_t i = inicio; i < fim; ++i) {
saida.push_back(static_cast<std::byte>(static_cast<unsigned char>(nome[i])));
}
if (ponto == std::string_view::npos) break;
inicio = ponto + 1;
}
saida.push_back(std::byte{0});
return saida;
}
std::expected<std::vector<std::byte>, Erro>
construir_consulta(std::string_view nome, std::uint16_t tipo,
std::uint16_t identificador) {
auto u16 = [](std::vector<std::byte>& v, std::uint16_t x) {
v.push_back(static_cast<std::byte>(x >> 8));
v.push_back(static_cast<std::byte>(x & 0xFF));
};
std::vector<std::byte> m;
u16(m, identificador);
u16(m, 0x0100); // RD = 1, o resolvedor recursivo faz o trabalho
u16(m, 1); // QDCOUNT
u16(m, 0); // ANCOUNT
u16(m, 0); // NSCOUNT
u16(m, 0); // ARCOUNT
const auto q = codificar_nome(nome);
if (!q) return std::unexpected(q.error());
m.insert(m.end(), q->begin(), q->end());
u16(m, tipo);
u16(m, 1); // QCLASS = IN
return m;
}
// ---------- analise ----------
struct NomeLido {
std::string nome;
std::size_t proximo; // offset logo apos o nome, no fluxo original
};
// Le um nome possivelmente comprimido. Um rotulo cujos dois bits altos sao 11
// e um ponteiro de 14 bits para outro ponto da mesma mensagem. Isso permite
// que uma mensagem maliciosa contenha um ciclo, e o limite de saltos existe
// para que o analisador termine em vez de girar para sempre.
std::expected<NomeLido, Erro> ler_nome(std::span<const std::byte> m, std::size_t off) {
constexpr int MAX_SALTOS = 16;
constexpr std::size_t MAX_NOME = 255;
std::string nome;
std::size_t proximo = 0;
bool saltou = false;
int saltos = 0;
for (;;) {
if (off >= m.size()) return std::unexpected(Erro::mensagem_curta);
const auto b = static_cast<std::uint8_t>(m[off]);
if ((b & 0xC0) == 0xC0) { // ponteiro
if (off + 1 >= m.size()) return std::unexpected(Erro::mensagem_curta);
if (++saltos > MAX_SALTOS) return std::unexpected(Erro::laco_de_ponteiros);
const std::size_t alvo =
(static_cast<std::size_t>(b & 0x3F) << 8) |
static_cast<std::uint8_t>(m[off + 1]);
if (alvo >= m.size()) return std::unexpected(Erro::ponteiro_fora);
if (!saltou) { proximo = off + 2; saltou = true; }
off = alvo;
continue;
}
if ((b & 0xC0) != 0x00) return std::unexpected(Erro::rotulo_invalido);
if (b == 0) { // fim do nome
if (!saltou) proximo = off + 1;
if (nome.empty()) nome = ".";
return NomeLido{nome, proximo};
}
if (off + 1 + b > m.size()) return std::unexpected(Erro::mensagem_curta);
if (nome.size() + b + 1 > MAX_NOME) return std::unexpected(Erro::nome_longo);
if (!nome.empty()) nome.push_back('.');
for (std::size_t i = 0; i < b; ++i) {
nome.push_back(static_cast<char>(m[off + 1 + i]));
}
off += 1 + b;
}
}
struct Registro {
std::string nome;
std::uint16_t tipo{};
std::uint32_t ttl{};
std::string valor;
};
struct Mensagem {
std::uint16_t identificador{};
std::uint16_t bandeiras{};
std::string pergunta;
std::vector<Registro> respostas;
};
constexpr std::string_view nome_do_tipo(std::uint16_t t) {
switch (t) {
case 1: return "A";
case 2: return "NS";
case 5: return "CNAME";
case 15: return "MX";
case 16: return "TXT";
case 28: return "AAAA";
default: return "?";
}
}
std::expected<Mensagem, Erro> analisar(std::span<const std::byte> m) {
auto u16 = [&](std::size_t o) -> std::uint16_t {
return static_cast<std::uint16_t>(
(static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(m[o])) << 8) |
static_cast<std::uint16_t>(std::to_integer<std::uint8_t>(m[o + 1])));
};
if (m.size() < 12) return std::unexpected(Erro::mensagem_curta);
Mensagem msg;
msg.identificador = u16(0);
msg.bandeiras = u16(2);
const auto qd = u16(4);
const auto an = u16(6);
std::size_t off = 12;
for (std::uint16_t i = 0; i < qd; ++i) {
auto n = ler_nome(m, off);
if (!n) return std::unexpected(n.error());
msg.pergunta = n->nome;
if (n->proximo > m.size() || m.size() - n->proximo < 4) {
return std::unexpected(Erro::mensagem_curta);
}
off = n->proximo + 4; // QTYPE e QCLASS
}
for (std::uint16_t i = 0; i < an; ++i) {
auto n = ler_nome(m, off);
if (!n) return std::unexpected(n.error());
off = n->proximo;
if (off > m.size() || m.size() - off < 10) {
return std::unexpected(Erro::mensagem_curta);
}
Registro r;
r.nome = n->nome;
r.tipo = u16(off);
r.ttl = (static_cast<std::uint32_t>(u16(off + 4)) << 16) | u16(off + 6);
const auto rdlength = u16(off + 8);
off += 10;
if (off > m.size() || rdlength > m.size() - off) {
return std::unexpected(Erro::mensagem_curta);
}
if (r.tipo == 1 && rdlength == 4) {
r.valor = std::format("{}.{}.{}.{}",
std::to_integer<int>(m[off]), std::to_integer<int>(m[off + 1]),
std::to_integer<int>(m[off + 2]), std::to_integer<int>(m[off + 3]));
} else if (r.tipo == 5 || r.tipo == 2) {
auto alvo = ler_nome(m, off);
if (!alvo) return std::unexpected(alvo.error());
if (alvo->proximo != off + rdlength) {
return std::unexpected(Erro::rdata_invalida);
}
r.valor = alvo->nome;
} else {
r.valor = std::format("{} bytes de rdata", rdlength);
}
off += rdlength;
msg.respostas.push_back(std::move(r));
}
return msg;
}
} // namespace dns
namespace {
// Resposta sintetica para www.example.com, com dois niveis de compressao:
// o nome da resposta 1 aponta para o offset 12, sua rdata monta "lb" mais um
// ponteiro para "example.com" no offset 16, e o nome da resposta 2 aponta para
// essa rdata, no offset 45.
constexpr std::uint8_t RESPOSTA[] = {
0x12, 0x34, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65,
0x03, 0x63, 0x6F, 0x6D, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00,
0x05, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2C, 0x00, 0x05, 0x02, 0x6C, 0x62,
0xC0, 0x10, 0xC0, 0x2D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3C,
0x00, 0x04, 0xCB, 0x00, 0x71, 0x0A,
};
// A mesma mensagem com um ponteiro que aponta para si mesmo, no offset 12.
constexpr std::uint8_t BOMBA[] = {
0x12, 0x34, 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01,
};
std::span<const std::byte> como_bytes(std::span<const std::uint8_t> v) {
return {reinterpret_cast<const std::byte*>(v.data()), v.size()};
}
std::string em_hexa(std::span<const std::byte> v) {
std::string s;
s.reserve(v.size() * 3);
for (const auto b : v) s += std::format("{:02X} ", std::to_integer<int>(b));
return s;
}
} // namespace
int main() {
const auto consulta = dns::construir_consulta("www.example.com", 1, 0x1234);
if (!consulta) {
std::println("falha ao construir consulta: {}", dns::descrever(consulta.error()));
return 1;
}
std::println("consulta: {} bytes", consulta->size());
std::println(" {}", em_hexa(*consulta));
const auto rotulo_longo = std::string(64, 'a') + ".example";
const auto consulta_invalida = dns::construir_consulta(rotulo_longo, 1, 0x1234);
std::println("consulta com rotulo de 64 bytes: {}",
consulta_invalida ? "aceita" : dns::descrever(consulta_invalida.error()));
const auto msg = dns::analisar(como_bytes(RESPOSTA));
if (!msg) {
std::println("falha ao analisar: {}", dns::descrever(msg.error()));
return 1;
}
std::println("");
std::println("resposta: {} bytes, ID {:#06x}, RCODE {}",
sizeof(RESPOSTA), msg->identificador, msg->bandeiras & 0x0F);
std::println(" pergunta: {}", msg->pergunta);
for (const auto& r : msg->respostas) {
std::println(" {:<16} {:<6} TTL {:>5} s -> {}",
r.nome, dns::nome_do_tipo(r.tipo), r.ttl, r.valor);
}
std::println("");
const auto bomba = dns::analisar(como_bytes(BOMBA));
std::println("mensagem com ponteiro circular: {}",
bomba ? "analisada" : std::format("recusada, {}", dns::descrever(bomba.error())));
std::vector<std::uint8_t> rdata_ruim(RESPOSTA, RESPOSTA + sizeof(RESPOSTA));
rdata_ruim[44] = 4; // declara quatro bytes para um nome que ocupa cinco
const auto registro_invasor = dns::analisar(como_bytes(rdata_ruim));
std::println("rdata que invade o registro seguinte: {}",
registro_invasor ? "analisada"
: std::format("recusada, {}",
dns::descrever(registro_invasor.error())));
return 0;
}
Compilado com MSVC 19.51 em /std:c++latest /W4 /WX /EHsc /O2, sem advertências, produz:
consulta: 33 bytes
12 34 01 00 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 6D 70 6C 65 03 63 6F 6D 00 00 01 00 01
consulta com rotulo de 64 bytes: rotulo longo demais
resposta: 66 bytes, ID 0x1234, RCODE 0
pergunta: www.example.com
www.example.com CNAME TTL 300 s -> lb.example.com
lb.example.com A TTL 60 s -> 203.0.113.10
mensagem com ponteiro circular: recusada, laco de ponteiros
rdata que invade o registro seguinte: recusada, rdata invalida
Vale acompanhar a resposta byte a byte, porque a compressão só faz sentido quando se vê o ponteiro apontando.
A pergunta ocupa os offsets $12$ a $32$, com www em $12$, example em $16$ e com em $24$. A primeira resposta começa em $33$ e seu nome é apenas C0 0C, isto é, um ponteiro para o offset $12$: www.example.com inteiro em dois bytes. Sua rdata, em $45$, é 02 6C 62 C0 10, ou seja, o rótulo lb seguido de um ponteiro para o offset $16$, que monta lb.example.com em cinco bytes. E o nome da segunda resposta é C0 2D, um ponteiro para o offset $45$, reaproveitando o nome que acabou de ser montado dentro da rdata anterior.
Sem compressão, esses três nomes ocupariam $17 + 16 + 16 = 49$ bytes; com compressão ocupam $2 + 5 + 2 = 9$. A economia é de $81{,}6\%$ nos nomes e de $37{,}7\%$ na mensagem inteira, que sairia de $106$ para os $66$ bytes observados. Em um serviço que responde milhões de consultas por segundo, essa redução pode se tornar material para banda e processamento.
Quatro decisões do código merecem comentário.
codificar_nome devolve erro antes de emitir um rótulo com mais de $63$ bytes ou um nome com mais de $255$. Sem essa verificação, o comprimento truncado poderia ocupar os dois bits reservados aos ponteiros e transformar uma consulta construída localmente em uma mensagem com outra estrutura. O programa exercita a fronteira com um rótulo de $64$ bytes e o recusa.
ler_nome devolve std::expected<NomeLido, Erro> porque falhar é o caso normal ao analisar dados de rede. Uma mensagem malformada não é uma condição excepcional: é o que um atacante envia de propósito, e a assinatura da função precisa dizer isso a quem a chama. O tipo obriga a lidar com o erro; um código de retorno inteiro, não.
O limite de saltos é a defesa contra a bomba de descompressão, e o programa o exercita: a constante BOMBA tem dezoito bytes e um ponteiro para si mesma, e o analisador a recusa em vez de girar. Registre que o limite precisa ser sobre o número de saltos, e não sobre o tamanho do nome: uma cadeia de ponteiros pode alternar entre dois pontos indefinidamente sem acrescentar um único caractere.
O campo proximo guarda o offset seguinte no fluxo original, e é atualizado apenas no primeiro salto. Sem isso, o chamador continuaria a leitura no lugar para onde o ponteiro apontou, e não depois do ponteiro, e leria os registros seguintes a partir de posições absurdas. É o tipo de defeito que produz um analisador funcionando em oitenta por cento das respostas.
Por fim, um nome armazenado em rdata precisa terminar exatamente no limite declarado por RDLENGTH. Verificar apenas se o nome cabe na mensagem permitiria que um rótulo malformado invadisse o registro seguinte. A checagem de rdata_invalida transforma esse limite de formato em uma condição explícita do analisador.
O laboratório abaixo põe em movimento a aritmética das Seções 3 e 6. Vale começar pelas configurações predefinidas e depois arrastar o TTL observando as três leituras ao mesmo tempo: a taxa de acerto, a carga no autoritativo e o limite de recuperação.
Quando o Lab 03 da suíte NetworkLabs estiver disponível, o experimento a fazer nele é outro e complementar: derrubar o servidor autoritativo e cronometrar por quanto tempo os clientes continuam funcionando apenas por causa do cache. É o compromisso da Seção 3 visto do lado bom.
10. Como auditar uma mudança de DNS
Uma mudança de DNS precisa ser verificada no arquivo de zona, na resolução observada de fora e no tempo necessário para desfazê-la. Considere a configuração abaixo para apontar um domínio a um balanceador:
Para apontar o domínio raiz para o load balancer, crie um registro CNAME no apex da zona:
example.com. 300 IN CNAME lb-prod-1234.elb.amazonaws.com.Isso garante que, se o endereço IP do balanceador mudar, a resolução continuará funcionando sem necessidade de alteração na zona.
O raciocínio está certo e a conclusão é proibida. A RFC 1034 não permite que um CNAME coexista com outros registros no mesmo nó, e o ápice é obrigado a ter SOA e NS. A configuração é inválida.
O que torna esse erro perigoso é o comportamento das ferramentas. Alguns provedores recusam o registro, e o problema aparece na hora, que é o melhor caso. Outros aceitam e o convertem silenciosamente em um ALIAS, o que funciona e produz semântica diferente da pedida, com TTL efetivo decidido pelo provedor. E um servidor autoritativo próprio pode aceitar o arquivo de zona e servir respostas inconsistentes, que resolvedores diferentes interpretam de formas diferentes. O mesmo erro tem três desfechos conforme a ferramenta, e apenas um deles avisa.
O verificador, em três camadas.
A sintaxe é a validação do arquivo de zona ou da chamada de API. Um verificador de zona detecta o CNAME no ápice, os registros duplicados, os TTLs fora de faixa e os nomes malformados. É barato, é automatizável em uma esteira e pega a maior parte dos disparates. O Artigo 9 vai transformar isso em portão de integração contínua.
A semântica é a única camada que vale de verdade aqui, e ela tem uma forma específica: resolver de fora, a partir de resolvedores independentes, e comparar com o valor esperado. Não olhe a configuração; consulte o nome. Faça isso a partir de ao menos três resolvedores distintos, porque um deles pode estar servindo cache antigo e concordar com você pelo motivo errado. Verifique o valor, o tipo e o TTL devolvido, que às vezes não é o configurado.
O raio de explosão tem, no DNS, uma característica que merece tratamento próprio: ele é assimétrico no tempo. Aplicar uma mudança errada leva segundos; desfazê-la pode levar um TTL inteiro. Com TTL de $3600$ s e expirações uniformemente distribuídas, um erro cometido às nove horas ainda atinge aproximadamente metade dos caches às nove e meia, mesmo com a correção publicada às nove e um minuto. A pergunta antes da alteração passa a ser: se estiver errado, quanto tempo levaremos para reverter e quantos usuários atravessarão esse intervalo? A primeira aproximação vem do TTL vigente, e é por isso que reduzi-lo com antecedência é procedimento e não superstição.
Exercício de auditoria. Considere o plano abaixo para migrar um serviço entre regiões. Encontre os três defeitos antes de ler a resposta.
- Reduzir o TTL do registro de 3600s para 60s.
- Imediatamente após, alterar o registro A para o novo endereço.
- Aguardar 60 segundos para a propagação completa.
- Validar acessando o domínio pelo navegador e confirmando que a nova versão responde.
O primeiro defeito é o passo 2. Reduzir o TTL não afeta as cópias já em cache, que carregam o valor antigo com o TTL antigo. Alterar o registro imediatamente depois faz parte dos resolvedores continuar servindo o endereço velho por até $3600$ s. É preciso esperar o TTL antigo inteiro entre os passos 1 e 2, exatamente como o exercício 4 da Seção 3 estabeleceu.
O segundo defeito é uma etapa ausente depois do passo 4. O plano reduz o TTL para $60$ s e nunca o restaura. A migração termina funcionalmente, mas deixa o autoritativo recebendo até sessenta vezes mais consultas que na configuração original de $3600$ s. Sessenta segundos pode limitar a propagação entre caches compatíveis; não autoriza esquecer o custo permanente da configuração transitória.
O terceiro defeito é o passo 4, e é o mais insidioso porque parece uma verificação. Testar pelo navegador do operador verifica um caminho, com um resolvedor e com todos os caches locais daquela máquina, que provavelmente foram aquecidos durante a preparação. É o teste mais provável de dar verde por engano. A validação correta consulta o nome a partir de vários resolvedores independentes e compara valor, tipo e TTL, o que também dá uma estimativa do estado real da propagação.
Três defeitos, três camadas. O primeiro morre na semântica, porque uma consulta a partir de fora mostraria o valor antigo ainda sendo servido. O segundo aparece na operação contínua, porque uma configuração transitória virou custo permanente. E o terceiro morre no método de verificação: um único caminho aquecido não representa a população de resolvedores.
11. Conclusão
Este artigo tratou de um sistema que quase todo mundo considera resolvido e quase ninguém sabe dimensionar.
A leitora agora calcula a taxa de acerto de um cache por $h = \lambda T/(1 + \lambda T)$ e sabe que ela depende do produto $\lambda T$, não do TTL isolado. Sabe que, sob expirações uniformemente distribuídas, a propagação cai linearmente e termina no TTL, o que torna obrigatória a espera do valor antigo antes de uma migração planejada. Sabe que reduzir o TTL por dez multiplica por dez a carga no autoritativo, enquanto a fatura segue os degraus da tarifa. Distingue os $93$ ms de uma resolução fria, os $3$ ms de um acerto no resolvedor e o valor inferior a $1$ ms de um cache local. Também sabe que o custo de uma cadeia de CNAME depende do estado das delegações em cache.
Há duas conclusões novas que mudam decisões de nuvem. Pesos governam respostas autoritativas, não porcentagens de requisições; por isso, a exposição de um canário precisa ser medida nos destinos. E a soma entre detecção e TTL é um limite construído para o pior caso, não uma observação automática de todo cliente. Camadas compatíveis preservam o TTL remanescente; somente uma política local que estenda esse valor cria tempo adicional.
Sabe, por fim, que o raio de explosão do DNS é assimétrico no tempo: publicar leva segundos, enquanto retirar uma resposta já armazenada depende de expiração.
Um fio ficou solto de propósito. A Seção 7 mencionou um incidente em que servidores autoritativos perfeitamente saudáveis desapareceram da Internet porque uma alteração de configuração retirou os anúncios de rota dos prefixos que os continham. Nada no formato DNS falhou; falhou a camada que decide por onde os pacotes vão. O próximo artigo entra no BGP e mostra como políticas entre organizações propagam anúncios de alcance sem que o protocolo-base forneça prova criptográfica da origem de cada rota.
Acrônimos e Abreviações neste artigo
| Acrônimo | Definição em Inglês | Tradução em Português |
|---|---|---|
API |
Application Programming Interface | Interface de Programação de Aplicações |
BGP |
Border Gateway Protocol | Protocolo de Roteamento de Borda |
DNS |
Domain Name System | Sistema de Nomes de Domínio |
DNSSEC |
Domain Name System Security Extensions | Extensões de Segurança do DNS |
DoH |
DNS over HTTPS | DNS sobre HTTPS |
DoT |
DNS over TLS | DNS sobre TLS |
IN |
Internet (classe de registro) | Internet |
QUIC |
QUIC (nome próprio) | QUIC |
RFC |
Request for Comments | Pedido de Comentários |
RTT |
Round-Trip Time | Tempo de Ida e Volta |
SOA |
Start of Authority | Início de Autoridade |
TCP |
Transmission Control Protocol | Protocolo de Controle de Transmissão |
TLD |
Top-Level Domain | Domínio de Topo |
TLS |
Transport Layer Security | Segurança da Camada de Transporte |
TTL |
Time To Live | Tempo de Vida |
UDP |
User Datagram Protocol | Protocolo de Datagrama de Usuário |
Referências
AMAZON WEB SERVICES. Amazon Route 53 Developer Guide: Choosing a Routing Policy. Disponível em: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy.html. Acesso em: 18 ago. 2026.
AMAZON WEB SERVICES. Amazon Route 53 Pricing. Disponível em: https://aws.amazon.com/route53/pricing/. Acesso em: 18 ago. 2026.
AMAZON WEB SERVICES. Values Specific for Weighted Records. Disponível em: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-values-weighted.html. Acesso em: 18 ago. 2026.
ARENDS, R.; AUSTEIN, R.; LARSON, M.; MASSEY, D.; ROSE, S. RFC 4033: DNS Security Introduction and Requirements. IETF, 2005. Disponível em: https://www.rfc-editor.org/rfc/rfc4033. Acesso em: 18 ago. 2026.
CLOUDFLARE. Understanding How Facebook Disappeared from the Internet. The Cloudflare Blog, 2021. Disponível em: https://blog.cloudflare.com/october-2021-facebook-outage/. Acesso em: 18 ago. 2026.
CONTAVALLI, C.; VAN DER GAAST, W.; LAWRENCE, D.; KUMARI, W. RFC 7871: Client Subnet in DNS Queries. IETF, 2016. Disponível em: https://www.rfc-editor.org/rfc/rfc7871. Acesso em: 18 ago. 2026.
DYN. Dyn Statement on 10/21/2016 DDoS Attack. 2016. Disponível em: https://cyber-peace.org/wp-content/uploads/2016/10/Dyn-Statement-on-10_21_2016-DDoS-Attack.pdf. Acesso em: 18 ago. 2026.
ELZ, R.; BUSH, R. RFC 2181: Clarifications to the DNS Specification. IETF, 1997. Disponível em: https://www.rfc-editor.org/rfc/rfc2181. Acesso em: 18 ago. 2026.
HOFFMAN, P.; MCMANUS, P. RFC 8484: DNS Queries over HTTPS (DoH). IETF, 2018. Disponível em: https://www.rfc-editor.org/rfc/rfc8484. Acesso em: 18 ago. 2026.
HU, Z.; ZHU, L.; HEIDEMANN, J.; MANKIN, A.; WESSELS, D.; HOFFMAN, P. RFC 7858: Specification for DNS over Transport Layer Security (TLS). IETF, 2016. Disponível em: https://www.rfc-editor.org/rfc/rfc7858. Acesso em: 18 ago. 2026.
INTERNET SYSTEMS CONSORTIUM. BIND 9 Administrator Reference Manual. Disponível em: https://bind9.readthedocs.io/. Acesso em: 18 ago. 2026.
MOCKAPETRIS, P. RFC 1034: Domain Names — Concepts and Facilities. IETF, 1987. Disponível em: https://www.rfc-editor.org/rfc/rfc1034. Acesso em: 18 ago. 2026.
MOCKAPETRIS, P. RFC 1035: Domain Names — Implementation and Specification. IETF, 1987. Disponível em: https://www.rfc-editor.org/rfc/rfc1035. Acesso em: 18 ago. 2026.
ROOT SERVER TECHNICAL OPERATIONS ASSOCIATION. Root Server Technical Operations. Disponível em: https://root-servers.org/. Acesso em: 18 ago. 2026.
VIXIE, P.; DAMAS, J.; GRAFF, M.; KOLKMAN, O. RFC 6891: Extension Mechanisms for DNS (EDNS(0)). IETF, 2013. Disponível em: https://www.rfc-editor.org/rfc/rfc6891. Acesso em: 18 ago. 2026.
Índice da Série: Redes para Engenharia de Software
- 1. A Pilha TCP/IP e a Física da Rede
- 2. Da Rede Local à Web: Dispositivos, Topologias e Serviços
- 3. Camada IP: Endereçamento, Sub-redes e Encaminhamento
- 4. Transporte: TCP, UDP e QUIC
- 5. Resolução de Nomes: DNS na Era da Nuvem (Você está aqui)
(Updated: )