Summary

  • RFC 2066 required an explicit positive acknowledgement even when the receiver was already using a requested character set; silence could no longer stand for success.
  • A simultaneous CHARSET REQUEST collision was not resolved symmetrically: the server had to reject the client's request, while the client had to answer the server's.
  • ACCEPTED proved receipt and selected a wire encoding for subsequent text. It did not prove correct bytes, successful translation, application use, authentication or a secure session.

Imagine a Telnet endpoint offering two character sets and then hearing nothing. Perhaps the peer ignored the message because the current encoding was already acceptable. Perhaps the request was lost. Perhaps the peer did not implement the subnegotiation correctly. The sender sees the same evidence in every case: silence.

RFC 2066 refused to make that ambiguity part of the protocol state. Published as an Experimental RFC in January 1997, it defined Telnet option 42, CHARSET, so a client and server could name the encoding of text and, optionally, exchange translation tables. Its most revealing choice was not the list of encodings. It was the decision that a character-set transition needed a receipt.

The base rule was sensible—and insufficient here

Telnet's ordinary option negotiation was designed around DO, DON'T, WILL and WON'T. RFC 854 made the syntax symmetric and treated simultaneous requests as mutual positive acknowledgements. It also warned that symmetry could create endless acknowledgement loops. A party should not answer an apparent request for a mode already in force, while a genuine request for change had to receive a response.

RFC 855 put richer parameter exchange behind that first agreement. The peers first agreed to discuss an option; then subnegotiation carried the parameters between IAC SB and IAC SE. The two layers solved different questions. DO CHARSET and WILL CHARSET established permission to negotiate. They did not say which character set would govern the next text byte.

RFC 2066 therefore departed from one possible reading of the old non-response rule. If the receiver was already sending and expecting a character set named in a new REQUEST, it still had to reply ACCEPTED. The RFC gave its reason directly: determinacy. The requester should not wait, time out and infer an answer. An ACCEPTED itself received no acknowledgement, so explicitness did not restart the loop.

A request was an ordered offer, not a declaration

Only a side that had received DO CHARSET and sent WILL CHARSET could issue a CHARSET REQUEST. The request listed one or more sets in preference order. Unless a name began with the private-use X- prefix, it had to be registered with IANA. The receiver remained free to choose a supported entry according to its own preferences.

That produced four bounded response paths. The receiver could acknowledge a set already in use; choose another supported set from the list; return a translation table if the requester had offered that facility; or reject the list when it could support none of it. A positive reply named one of the proposed sets. A negative reply acknowledged the request but refused every proposed set for that exchange.

Both ACCEPTED and REJECTED terminated the current subnegotiation. This is more precise than calling either one a generic success or failure. ACCEPTED changed the encoding obligation for subsequent text. REJECTED kept the proposed encodings out of force. Neither reply described why an implementation had that capability, nor did it make the answer permanent.

Two simultaneous requests needed one designated loser

The difficult case appeared when both eligible sides sent CHARSET REQUEST before receiving the other's message. A new request was not a valid answer to an outstanding request. Without another rule, each endpoint could wait for a terminal reply while holding a request from its peer.

RFC 2066 broke the symmetry by role. The server had to send a negative acknowledgement to the client's request. The client had to respond to the server's request. One proposed transition was closed; the other could reach ACCEPTED, REJECTED or the translation-table path. The rule did not claim that the server's preference was morally superior. It simply assigned different actions to stable roles so the same collision produced one terminating sequence at both ends.

The RFC's worked discussion allowed another round after rejection. A server that preferred the client to use the application's character set could reject the client's first proposal and then issue its own request. If that was also rejected, the server could return to one of the client's earlier sets. Each round still needed its own terminal response. Preference could evolve; the receipt boundary could not be skipped.

The encoding boundary sat inside the byte stream

After ACCEPTED, each side had to encode following text in the selected set. During a subnegotiation, data should be queued and released only after the exchange terminated. The acknowledgement was therefore also a sequencing marker: it separated bytes governed by the old understanding from bytes governed by the new one.

The scope remained narrow. Translation applied to text, not Telnet commands, and only when BINARY mode was active. Without BINARY, data remained NVT ASCII. For block-mode terminals, RFC 2066 recommended the End of Record option so an implementation could still identify record boundaries. Selecting a character set did not erase the surrounding Telnet contract.

Translation tables had their own receipts. TTABLE-ACK confirmed successful receipt and activated the mapped encoding. TTABLE-NAK asked for retransmission, but repeated failure was supposed to end in TTABLE-REJECTED or CHARSET REJECTED, not an unlimited exchange of futile messages. Here again, the design preferred an observable terminal state to exhausted patience.

A receipt was useful because its claim was small

A captured ACCEPTED supports a specific historical statement: the peer received a particular request and selected one of its listed character-set names for following text. It does not prove that following bytes conformed, that a translation table was correct, that the application decoded the text, or that the user completed a task. A captured REJECTED proves receipt and refusal of that list in that round, not permanent incapacity.

The option also supplied no security halo. RFC 2066's Security Considerations section said security issues were not discussed. Negotiating an encoding did not authenticate either endpoint, authorize an application, encrypt the session or protect text integrity. A deterministic state transition can be correct while the channel carrying it remains insecure.

Nor does the publication record establish adoption. RFC 2066 was Experimental, and the current IANA registry still identifies code 42 as CHARSET. Those facts document specification and assignment. They do not count implementations, demonstrate interoperability or show that a named product ever used the collision rule.

Read through Lu Heng's later Minimum Initial Specification framework, the mechanism is a compact example of a thin common layer doing only the work that must be shared: define request eligibility, terminal replies, a byte-transition point and a collision rule that each endpoint can apply locally. That is an editorial comparison, not evidence of the RFC author's intent. The harder historical lesson is simpler. Where silence can mean several things, interoperability begins by making the missing receipt explicit.

Sources