RFC 1143
The Q Method of Implementing TELNET Option Negotiation
RFC 1143 ๐ The Q Method of Implementing TELNET Option Negotiation
Part 1 of 3 ๐ more to come!
okay bestie, this is RFC 1143 ๐ , written by D. Bernstein at NYU for the Network Working Group โจ, dated February 1990. Its title block doesn't list anything it obsoletes or updates, and there's no category tag up top, but the Status section tells us it's an experimental method of implementing a protocol ๐ง, not a protocol spec.
Status of This Memo ๐ธ
so this doc is about how you could handle option negotiation in the Telnet protocol (that's RFC 854) ๐ฅ๏ธ. It doesn't propose any changes to TELNET. It only covers the implementation of one feature, not the whole protocol. It's not a protocol specification, it's an experimental method, and it's not a recommendation from the Telnet Working Group of the Internet Engineering Task Force (IETF) ๐. It's copyright 1990, Daniel J. Bernstein, but you can distribute it in its original form, unlimited ๐.
1. Introduction โจ
this RFC levels up the RFC 854 [7] option negotiation rules and guidelines ๐ฅ. Those rules aren't enough by themselves to stop every option negotiation loop ๐. It also shows an example of correct implementation ๐ฆ.
DISCUSSION:
the two things implementors care about most are 1. the loop examples below and 2. the TELNET state machine that prevents loops ๐ง .
- Implementors of TELNET should read the loop examples and watch out, because preventing loops is a nontrivial task ๐ญ.
- Section 7 of this RFC shows by example a working method of avoiding loops ๐. It lays out the state information you must keep about each side of each option. It shows what to do in each state when you receive WILL/WONT/DO/DONT from the network, and when the user or process asks for an option to be enabled or disabled. An implementor who follows the procedures in that example doesn't need to worry about compliance with this RFC or with a large chunk of RFC 854 ๐.
In short, all implementors should be familiar with TELNET loops, and some implementors may wish to use the pre-written example here when writing a new TELNET implementation ๐.
NOTE: Reading This Document ๐
here's the compliance tea โ. A TELNET implementation is not compliant with this RFC if it doesn't satisfy every rule marked MUST. It is compliant if it satisfies all the MUST rules. And if it's compliant, it's unconditionally compliant when it also satisfies all the rules marked SHOULD, and conditionally compliant otherwise. Rules marked MAY are optional ๐ซถ.
Options are almost always negotiated separately for each side of the connection ๐ฆ. The option on one side is its own thing, separate from the option on the other side. In this document, "the" option referred to by a DONT/WONT or DO/WILL is really two options, only combined for semantic convenience. Each sentence could be split into two, one with the words before the slash and one with the words after the slash.
An implementor should be able to tell whether an implementation complies with this RFC without reading any text marked DISCUSSION ๐. An implementor should also be able to build option negotiation machinery that complies with both this RFC and RFC 854, using just the information in Section 7 โจ.
2. RFC 854 Option Negotiation Requirements ๐ฅ
straight from RFC 854: a TELNET implementation MUST obey a refusal to enable an option. Like, if it gets a DONT/WONT back after sending a WILL/DO, it MUST NOT enable the option ๐ .
DISCUSSION:
when RFC 854 hints that the other side can say no to an enable request, it means you have to accept that no ๐ .
So it MUST remember that it's negotiating a WILL/DO ๐ง . That negotiation state MUST be kept separate from the enabled state and from the disabled state. While it's in the negotiation state, any effects of having the option enabled MUST NOT be used.
If it gets WONT/DONT while the option is enabled, it MUST respond DONT/WONT respectively and disable the option ๐ซ . It MUST NOT start a DO/WILL negotiation for an option that's already enabled, or a DONT/WONT negotiation for one that's disabled. It MUST NOT respond to a negotiation like that. But it MUST respond to a negotiation that does propose changing the status quo ๐ธ.
DISCUSSION:
lots of existing implementations answer a rejection by confirming it. So if they send WILL and get a DONT, they send a WONT back ๐. People have read this as acceptable under a certain (strained) reading of RFC 854. But allowing this possibility seriously complicates the later rules, and there doesn't seem to be any use for the wasted bandwidth and processing ๐. Also, an implementation that complies with this RFC will simply ignore the extra WONT if the other side sends it ๐ซถ.
The implementation MUST NOT automatically answer a rejection by sending a brand new request ๐ . As a rule of thumb, new requests should go out either at the start of a connection or in response to an external stimulus, i.e., input from the human user or from the process behind the server ๐.
A TELNET implementation MUST refuse (DONT/WONT) any request to enable an option whose appropriate protocol specification it doesn't comply with ๐ซ.
DISCUSSION:
RFC 854 doesn't say this as strongly, but any other move would be counterproductive ๐ค. This rule shows up in Requirements for Internet Hosts [6, Section 3.2.2], and it's repeated here for completeness.
3. Rule: Remember DONT/WONT requests ๐ฆ
A TELNET implementation MUST remember starting a DONT/WONT negotiation ๐ง .
DISCUSSION:
it isn't clear from RFC 854 whether TELNET must remember beginning a DONT/WONT negotiation ๐ค. There don't seem to be any reasons to remember starting one:
- The argument for remembering a DO/WILL negotiation (viz., the state of negotiating for enabling means different things for the data stream than the state of having the option enabled) doesn't apply.
- There's no choice for the other side in responding to a DONT/WONT. The option is going to end up disabled.
- If we just disable the option right away and forget about negotiating, we'll ignore the WONT/DONT response, since the option is already disabled.
Unfortunately, that conclusion is wrong ๐. Here's a TELNET conversation between two parties, "us" and "him". (If you want a different view, you could sort the steps into chronological order yourself ๐งฉ.)
LOOP EXAMPLE 1
Both sides know that the option is on.
On his side:
1 He decides to disable. He sends DONT and disables the option.
2 He decides to reenable. He sends DO and remembers he is
negotiating.
5 He receives WONT and gives up on negotiation.
6 He decides to try once again to reenable. He sends DO and
remembers he is negotiating.
7 He receives WONT and gives up on negotiation.
For whatever reason, he decides to agree with future requests.
10 He receives WILL and agrees. He responds DO and enables the
option.
11 He receives WONT and sighs. He responds DONT and disables the
option.
(repeat 10 and then 11, forever)
On our side:
3 We receive DONT and sigh. We respond WONT and disable the
option.
4 We receive DO but disagree. We respond WONT.
8 We receive DO and decide to agree. We respond WILL and enable
the option.
9 We decide to disable. We send WONT and disable the option.
For whatever reason, we decide to agree with future requests.
12 We receive DO and agree. We send WILL and enable the option.
13 We receive DONT and sigh. We send WONT and disable the option.
(repeat 12 and then 13, forever)
Both sides followed RFC 854, and yet we still end up in an option negotiation loop ๐ฑ. DONT DO DO, then DO DONT, go round and round forever in one direction, while WONT WONT, then WILL WONT, do the same in the other direction ๐. Steps 1 and 9 are what cause this loop, which is why this section has its rule ๐. Section 6 below discusses whether "negotiate for disable" and "negotiate for enable" need separate states, or whether a single "negotiate" state is enough ๐ฎ.
4. Rule: Prohibit new requests before completing old negotiation โจ
A TELNET implementation MUST NOT initiate a new WILL/WONT/DO/DONT request about an option that is under negotiation, i.e., for which it has already made such a request and not yet received a response. ๐
โโ๏ธ
DISCUSSION:
Okay so RFC 854 doesn't really say whether you can send new requests about an option that's already mid-negotiation ๐ค And honestly, banning "option typeahead" feels kinda harsh, but here's the thing: it goes wrong fast. Look at this:
LOOP EXAMPLE 2
Suppose an option is disabled, and we decide in quick
succession to enable it, disable it, and reenable it. We send
WILL WONT WILL and at the end remember that we are negotiating.
The other side agrees with DO DONT DO. We receive the first DO,
enable the option, and forget we have negotiated. Now DONT DO
are coming through the network and both sides have forgotten
they are negotiating; consequently we loop.
Ngl that's a whole mess ๐ Both sides forget they're negotiating, and then the DONT and DO just keep bouncing around forever. ๐
Basically, every possible TELNET loop ends up looking the same: WILL WONT (or WONT WILL, or WILL WONT WILL WONT, etc.) floats through the network while both sides think negotiation is over. The response is DO DONT, and we loop forever. TELNET implementors are encouraged to build any option that can spot a loop like this and cut it off. For example, a way to explicitly tell requests apart from acknowledgments would do the job. But no such option existed as of February 1990. ๐ญ
This one is a big deal in real life too. Most combos of existing user-server TELNET implementations do get stuck in an infinite loop if you quickly ask them a few times to enable and then disable an option. ๐ It matters even more now that LINEMODE [4] is out, since LINEMODE is the first option that tends to fire off rapidly changing requests during normal use. So yeah, a new rule is definitely needed. ๐
Some people tried to stop the several-alternating-requests problem by keeping more detailed state than YES/NO/WANTwhatever, like a state that records every outstanding request. Dave Borman proposed a scheme [2] that seems to work, won't blow up even if both sides start several requests at once, and appears to stop option negotiation loops. The catch is that fully analyzing it is kind of tough, because TELNET would no longer be a finite-state automaton. He implemented it in the latest BSD telnet version [5], but as of May 1989 he didn't plan to publish it for others to use [3]. ๐ซฃ
So the author decided to keep TELNET's finite-state property, for robustness, and because the result is easy to prove works. โจ That's why we have the rule above.
A stricter option would be to buffer all data and do absolutely nothing until the response comes back. There's no obvious reason to do that, though some existing TELNET implementations do it anyway at the start of a connection, when most options get negotiated. ๐คทโโ๏ธ
5. How to reallow the request queue
DISCUSSION:
The rule above stops more than one request from being queued up through the network. BUT it's still possible to queue new requests inside the TELNET implementation, so "option typeahead" is basically back, just in a different way. ๐ฆ
The obvious approach is to keep a list of requests that have been made but not yet sent, so that when one negotiation finishes, the next one can start right away. So while negotiating for a WILL, TELNET could buffer the user's requests for WONT, then WILL again, then WONT, then WILL, then WONT, as many as wanted.
Ugh, but that needs a dynamic and potentially unmanageable buffer. ๐ฉ Here's the good part, though: the restrictions on possible requests mean the list can only ever alternate between WONT and WILL. It's wasteful to enable an option and then disable it, just to enable it again. We might as well just enable it in the first place. The few possible exceptions don't outweigh the huge simplification you get from only remembering the last few entries on the queue. ๐ง โจ
To be more precise: during a WILL negotiation, the only sensible queues are WONT and WONT WILL, and it's the same in reverse during a WONT negotiation. For simplicity, the Q method does not allow the WONT WILL possibility. ๐
โโ๏ธ
So now we're left with a queue that's either empty or holds the opposite of the current negotiation. When we get a reply to the negotiation, if the queue says the option should change, we send the opposite request immediately and empty the queue. This doesn't conflict with the RFC 854 rule about automatic regeneration of requests, because these new requests are just the delayed effects of user or process commands. ๐
An implementation SHOULD support the queue, where support is defined by the rules following.
If it does support the queue, and an option is currently under negotiation, it MUST NOT handle a new request from the user or process to switch that option's state by sending a new request through the network. Instead, it MUST remember internally that the new request was made.
If the user or process makes a second new request to switch back again while the original negotiation is still incomplete, the implementation SHOULD handle it simply by forgetting the previous one. The third request SHOULD be treated like the first, and so on. In any case, these further requests MUST NOT generate immediate requests through the network. ๐
When the option negotiation completes, if the implementation is remembering a request internally and that request is for the opposite state to the result of the completed negotiation, then the implementation MUST act as if that request had been made after the negotiation completed. It SHOULD thus immediately generate a new request through the network. ๐
The implementation MAY provide a method for turning support for the queue off and back on. In that case, it MUST default to having the support turned on. Furthermore, when support is turned off, if the implementation is remembering a new request for an outstanding negotiation, it SHOULD keep remembering it and then deal with it at the close of the outstanding negotiation, as if support were still turned on through that point. ๐
DISCUSSION:
The author believes this queue system fully restores TELNET's functionality, and it's intended to. ๐ Dave Borman also gave some informal analysis of this issue [1]. The biggest possible problem to note is that certain options which might need buffering could be slowed down by the queue. The author thinks buffering delays are independent of the implementation method, so the Q Method doesn't cause any problems. Examples back that up. ๐ธ
6. Rule: Separate WANTNO and WANTYES
Implementations SHOULD separate any states of negotiating WILL/DO from any states of negotiating WONT/DONT. ๐ฆ
DISCUSSION:
You can keep a working TELNET implementation if you simplify the NO/YES/WANTNO/WANTYES states down to NO/YES/WANT. ๐
BUT in a hostile environment that's a bad idea. It means handling a DO/WILL response to a WONT/DONT can't be done correctly. ๐จ Plus it doesn't really simplify the code, and whatever simplicity you gain gets eaten up by the extra complexity you need for the queue. Not it. ๐
7. Example of Correct Implementation
To make writing TELNET implementations easier, the author gives a precise example of the response a compliant TELNET implementation could give in each possible situation. All TELNET implementations compliant with this RFC SHOULD follow the procedures shown here. ๐ง
EXAMPLE STATE MACHINE
FOR THE Q METHOD OF IMPLEMENTING TELNET OPTION NEGOTIATION
There are two sides, we (us) and he (him). We keep four variables:
us: state of option on our side (NO/WANTNO/WANTYES/YES)
usq: a queue bit (EMPTY/OPPOSITE) if us is WANTNO or WANTYES
him: state of option on his side
himq: a queue bit if him is WANTNO or WANTYES
An option is enabled if and only if its state is YES. Note that us/usq and him/himq could be combined into two six-choice states. ๐ค
"Error" below means that producing diagnostic information may be a good idea, though it isn't required.
Upon receipt of WILL, we choose based upon him and himq:
NO If we agree that he should enable, him=YES, send
DO; otherwise, send DONT.
YES Ignore.
WANTNO EMPTY Error: DONT answered by WILL. him=NO.
OPPOSITE Error: DONT answered by WILL. him=YES*,
himq=EMPTY.
WANTYES EMPTY him=YES.
OPPOSITE him=WANTNO, himq=EMPTY, send DONT.
* This behavior is debatable. DONT will never be answered by WILL over a reliable connection between TELNETs compliant with this RFC, so this was chosen (1) not to generate further messages, because if we know we're dealing with a noncompliant TELNET we shouldn't trust it to be sensible; (2) to empty the queue sensibly. ๐คจ
Upon receipt of WONT, we choose based upon him and himq:
NO Ignore.
YES him=NO, send DONT.
WANTNO EMPTY him=NO.
OPPOSITE him=WANTYES, himq=NONE, send DO.
WANTYES EMPTY him=NO.*
OPPOSITE him=NO, himq=NONE.**
* Here is the only spot a length-two queue could be useful. After a WILL negotiation was refused, a queue of WONT WILL would mean to request the option again. This seems like too little utility and too much potential waste, since there's little chance the other side will change its mind immediately. ๐
** Here we don't have to generate another request, because we've been "refused into" the correct state anyway. ๐ซถ
If we decide to ask him to enable:
NO him=WANTYES, send DO.
YES Error: Already enabled.
WANTNO EMPTY If we are queueing requests, himq=OPPOSITE;
otherwise, Error: Cannot initiate new request
in the middle of negotiation.
OPPOSITE Error: Already queued an enable request.
WANTYES EMPTY Error: Already negotiating for enable.
OPPOSITE himq=EMPTY.
If we decide to ask him to disable:
NO Error: Already disabled.
YES him=WANTNO, send DONT.
WANTNO EMPTY Error: Already negotiating for disable.
OPPOSITE himq=EMPTY.
WANTYES EMPTY If we are queueing requests, himq=OPPOSITE;
otherwise, Error: Cannot initiate new request
in the middle of negotiation.
OPPOSITE Error: Already queued a disable request.
We handle the option on our side by the same procedures, with DO-WILL, DONT-WONT, him-us, and himq-usq swapped. ๐๐
8. References โจ
okay bestie, this is the bibliography ๐ every source the spec leans on, with all its details. ngl the private communications are giving mystery ๐ต๏ธโโ๏ธ
[1] Borman, D., private communication, April 1989.
[2] Borman, D., private communication, May 1989.
[3] Borman, D., private communication, May 1989.
[4] Borman, D., Editor, "Telnet Linemode Option", RFC 1116, Cray Research, August 1989.
[5] Borman, D., BSD Telnet Source, November 1989.
[6] Braden, R., Editor, "Requirements for Internet Hosts -- Application and Support", RFC 1123, USC/Information Sciences Institute, October 1989.
[7] Postel, J., and J. Reynolds, "Telnet Protocol Specification", RFC 854, USC/Information Sciences Institute, May 1983.
9. Acknowledgments ๐
shoutout to Dave Borman ๐ซถ who gave super helpful comments. the whole thing is better because of him, no cap โจ
Author's Address ๐
Daniel J. Bernstein
5 Brewster Lane
Bellport, NY 11713
Phone: 516-286-1339
Email: brnstnd@acf10.nyu.edu
๐ฆ where this page comes from
- ietf: rfc1143 1990-02
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms