Resumo

  • O RFC 2066 exigia ACCEPTED mesmo quando o receptor já utilizava um conjunto solicitado: silêncio não podia mais representar sucesso.
  • Dois CHARSET REQUEST simultâneos eram resolvidos por função: o servidor rejeitava o pedido do cliente, e o cliente respondia ao pedido do servidor.
  • A resposta comprovava recebimento e escolhia uma codificação para o texto seguinte; não comprovava bytes corretos, tradução fiel, uso pela aplicação, autenticação nem segurança.

Imagine um terminal Telnet oferecendo dois conjuntos de caracteres e não recebendo resposta. Talvez o par já use um deles e interprete a regra antiga como uma ordem para ficar calado. Talvez a mensagem tenha se perdido. Talvez a subnegociação esteja implementada incorretamente. Do ponto de vista de quem espera, as três situações produzem exatamente a mesma evidência: silêncio.

Publicado como Experimental em janeiro de 1997, o RFC 2066 recusou-se a transformar essa ambiguidade em estado de protocolo. O documento definiu a opção 42 do Telnet, CHARSET, para que cliente e servidor pudessem nomear a codificação do texto e, opcionalmente, trocar tabelas de tradução. Sua escolha mais reveladora não foi a lista de codificações. Foi exigir um recibo para a transição.

A regra contra ciclos era sensata — mas insuficiente aqui

A negociação comum do Telnet usava DO, DON'T, WILL e WON'T. O RFC 854 deu simetria à sintaxe e tratou pedidos simultâneos como confirmações positivas mútuas. Também alertou que a própria simetria podia criar uma sequência infinita de confirmações. Um lado não deveria responder a uma aparente solicitação de um modo já vigente; uma solicitação real de mudança, porém, precisava de resposta.

O RFC 855 colocou a troca de parâmetros depois desse primeiro acordo. Os pares primeiro aceitavam discutir uma opção; em seguida, a subnegociação levava parâmetros entre IAC SB e IAC SE. As camadas respondiam a perguntas diferentes. DO CHARSET e WILL CHARSET permitiam negociar, mas ainda não determinavam como interpretar o próximo byte de texto.

Por isso, o RFC 2066 não permitiu aplicar o silêncio ao pedido de charset. Se o receptor já enviava e esperava um conjunto incluído no novo REQUEST, ainda assim tinha de responder ACCEPTED; não podia ignorar a mensagem. O motivo declarado era a determinação: o solicitante não deveria esperar, atingir um timeout e inferir a resposta. Como um ACCEPTED não recebia confirmação adicional, explicitar o recibo não reabria o ciclo.

Uma lista ordenada era uma oferta, não uma declaração

Somente o lado que tivesse recebido DO CHARSET e enviado WILL CHARSET podia emitir um CHARSET REQUEST. O pedido continha um ou mais nomes, normalmente em ordem de preferência. Exceto pelos nomes privados iniciados por X-, eles deveriam estar registrados na IANA. O receptor continuava livre para escolher uma opção suportada de acordo com suas próprias preferências.

Isso produzia quatro saídas limitadas. O receptor podia confirmar o conjunto já em uso; escolher outro conjunto suportado da lista; devolver uma tabela de tradução quando o solicitante oferecesse esse recurso; ou rejeitar a lista quando não suportasse nenhum item. Uma resposta positiva nomeava um conjunto proposto. Uma resposta negativa comprovava o recebimento, mas recusava todas as propostas daquela rodada.

Tanto ACCEPTED quanto REJECTED encerravam a subnegociação em andamento. O primeiro alterava a obrigação de codificar o texto posterior. O segundo mantinha as codificações propostas fora de vigor. Nenhum dos dois explicava toda a capacidade da implementação nem impedia uma proposta futura.

Pedidos simultâneos precisavam de um perdedor designado

O caso difícil ocorria quando os dois lados habilitados enviavam CHARSET REQUEST antes de receber a mensagem do outro. Um novo pedido não era resposta válida para um pedido pendente. Sem outra regra, ambos poderiam esperar uma resposta terminal enquanto mantinham em mãos a solicitação do par.

O RFC 2066 rompeu a simetria pelas funções. O servidor precisava enviar confirmação negativa ao pedido do cliente. O cliente precisava responder ao pedido do servidor. Uma transição proposta era encerrada; a outra podia chegar a ACCEPTED, REJECTED ou à rota da tabela de tradução. A norma não dizia que a preferência do servidor era superior. Ela apenas atribuía ações diferentes a funções estáveis, para que ambos calculassem a mesma sequência terminal.

Uma rejeição ainda podia ser seguida por outra rodada. Um servidor que preferisse o conjunto de caracteres da aplicação poderia rejeitar a primeira proposta e então enviar a sua. Se ela também fosse rejeitada, o servidor poderia voltar a um conjunto oferecido antes pelo cliente. Cada rodada exigia sua própria resposta final. A preferência podia mudar; a fronteira do recibo não podia desaparecer.

A resposta criava uma fronteira no fluxo de bytes

Depois de ACCEPTED, cada lado precisava codificar o texto seguinte com o conjunto selecionado. Durante uma subnegociação, os dados deveriam ficar em fila e ser liberados apenas depois do término da troca. A resposta funcionava também como marcador de sequência entre bytes sujeitos ao entendimento antigo e bytes sujeitos ao novo.

O alcance era estreito. A tradução se aplicava ao texto, não aos comandos do Telnet, e apenas com o modo BINARY ativo. Sem BINARY, os dados continuavam em NVT ASCII. Para terminais em modo de bloco, o RFC recomendava a opção End of Record para preservar limites de registro. Escolher uma codificação não eliminava o restante do contrato Telnet.

As tabelas de tradução tinham seus próprios recibos. TTABLE-ACK confirmava o recebimento e ativava o mapeamento. TTABLE-NAK solicitava retransmissão, mas falhas repetidas deveriam terminar em TTABLE-REJECTED ou CHARSET REJECTED, não em uma troca ilimitada de mensagens inúteis. Também nesse caso, o desenho preferia um estado terminal observável à paciência esgotada.

O recibo era útil porque sua alegação era pequena

Um ACCEPTED capturado sustenta uma afirmação histórica específica: o par recebeu aquele pedido e escolheu um dos nomes listados para o texto seguinte. Não demonstra que os bytes posteriores estavam corretos, que uma tabela traduzia fielmente, que a aplicação consumiu o texto nem que o usuário concluiu uma tarefa. Um REJECTED prova recebimento e recusa daquela lista naquela rodada, não incapacidade permanente.

A opção tampouco oferecia uma camada de segurança. A seção Security Considerations do RFC 2066 diz que questões de segurança não são discutidas. Negociar uma codificação não autentica os pares, não autoriza uma aplicação, não cifra a sessão e não protege a integridade do texto. Uma transição de estado pode ser determinística mesmo quando o canal permanece inseguro.

O registro atual da IANA ainda identifica o código 42 como CHARSET, e o RFC conserva a categoria Experimental. Esses fatos documentam especificação e atribuição; não medem adoção nem provam interoperabilidade de produtos. Lido à luz do quadro posterior de Lu Heng sobre uma especificação inicial mínima, o mecanismo é um exemplo compacto de uma camada comum que faz apenas o trabalho compartilhado: define elegibilidade, respostas terminais, fronteira de bytes e uma regra de colisão aplicável localmente. Essa é uma comparação editorial, não evidência da intenção do autor do RFC. A lição histórica mais segura é mais simples: quando silêncio pode significar várias coisas, a interoperabilidade começa tornando explícito o recibo que faltava.

Fontes