Telnet Q State Option Logic RFC1143 example code - explaination sought

Viewed 126

I need/want to implement a RFC1143 compliant Telnet sub-option negotiation system in the MUD client I am coding for but I am having difficulty translating the methodology in Section 7 into workable C/C++ code. I have, as the text suggests as one way to do it, two six state enums - I understand that given the bit patterns chosen an option is considered to be active if, and only if, for a TelnetQStateFlag instance state , state == QStateYes:

    enum TelnetQStateFlag {
                                      // +-----If set then we have a queued
                                      // |     request for the opposite state
                                      // |+----If set then we want the opposite
                                      // ||+---Must be only one set for option
                                      // |||   to be active
                      QStateNo = 0x0, // 000 - No
                  QStateWantNo = 0x2, // 010 - Want No and we/he do not want to change to Yes
                 QStateWantYes = 0x3, // 011 - Want Yes and we/he do not want to change to No
                     QStateYes = 0x1, // 001 - Yes
    QStateWantYesQueueOpposite = 0x7, // 111 - Yes but we/he wants to change to no
     QStateWantNoQueueOpposite = 0x6  // 110 - No but we/he wants to change to yes
    };

    TelnetQStateFlag[256] mMyOptions;
    TelnetQStateFlag[256] mHisOptions;

By default/design/magic all the options will default to the QStateNo but I can see that I should, on initialisation set the ones (specific values of the size_t variable optionIndex) that MY end is prepared to handle by setting mMyOptions[optionIndex] = QStateWantYes and similarly the ones that we are prepared to accept from HIM as mHisOptions[optionIndex] = QStateWantYes.

Can someone talk me through - slowly - how to handle requests from one party to negotiate to control an option at the other end - both for the cases where the other end will and will not accept such an option; and how this works for both directions possibly in an overlapping situation?

I have been through and starting from the referred to RFC I have edited it to express the transitions in six-states:

There are two sides, we (us) and he (him).  Originally RFC1143 kept two
sets of two 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 below us/usq and him/himq has been combined into two six-choice
states:    

∙ NO              default (starting) state
∙ WANTYES         no pending request for this option to be enabled
∙ WANTYESQUEUEDNO already requested for this option to be enabled  
∙ WANTNO          no pending request for this option to be disabled  
∙ WANTNOQUEUEDYES already requested for this option to be disabled  
∙ YES             state is enabled - ONLY state in which option is ACTIVE

"ERROR" below means that producing diagnostic information may be a good idea,
though it isn't required.

Upon receipt of WILL(for his state){DO(for our state)}, we choose based upon
him{us} state:

∘ NO (if we agree)        ==> YES and send DO{WILL}
∘ NO (if we do not agree) ==> NOCHANGE but still send DONT{WONT}
∘ YES                     ==> NOCHANGE
∘ WANTNO                  ==> NO; ERROR: DONT{WONT} answered by WILL{DO}
∘ WANTNOQUEUEDYES         ==> WANTYES; ERROR: DONT{WONT} answered by WILL{DO}†
∘ WANTYES                 ==> YES
∘ WANTYESQUEUEDNO         ==> WANTNO and send DONT{WONT}

† This behaviour is debatable; DONT{WONT} will never be answered by WILL{DO}
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 non-compliant TELNET we shouldn't trust it to be sensible;
  2. to empty the queue sensibly.

Upon receipt of WONT{DONT}, we choose based upon him{us} state:
∙ NO                      ==> NOCHANGE
∙ YES                     ==> NO and send DONT{WONT}
∙ WANTNO                  ==> NO
∙ WANTNOQUEUEDYES         ==> WANTYES and send DO{WILL}
∙ WANTYES                 ==> NO ‡
∙ WANTYESQUEUEDNO         ==> NO ※

‡ Here is the only spot a length-two queue could be useful; after a WILL{DO}
negotiation was refused, a queue of WONT{DONT} WILL{DO} would mean to request
the option again. This seems of too little utility and too much potential
waste; there is little chance that the other side will change its mind
immediately.

※ Here we don't have to generate another request because we've{he's} been
"refused into" the correct state anyway.

If we decide to ask him to enable {we want to enable ourself}:
∘ NO              ==> WANTYES and send DO{WILL}
∘ YES             ==> NOCHANGE; ERROR: already enabled
∘ WANTNO          ==> WANTNOQUEUEDYES
∘ WANTNOQUEUEDYES ==> NOCHANGE; ERROR: cannot initiate new request in the
                      middle of negotiation.
∘ WANTYES         ==> NOCHANGE; ERROR: already negotiating for enable
∘ WANTYESQUEUEDNO ==> WANTYES

If we decide to ask him to disable {we want to disable ourself}:
∙ NO              ==> NOCHANGE; ERROR: already disabled
∙ YES             ==> WANTNO and send DONT{WONT}
∙ WANTNO          ==> NOCHANGE; error: already negotiating for disable
∙ WANTNOQUEUEDYES ==> WANTNO
∙ WANTYES         ==> WANTYESQUEUEDNO
∙ WANTYESQUEUEDNO ==> NOCHANGE; error: already queued a disable request
                      and must not initiate new request in the middle of
                      negotiation

We handle the option on our side by the same procedures, with DO<->WILL,
DONT<->WONT, him<->us swapped as shown above.

Update 2017/07/26: One aspect that is a little confusing to me is the case when he has an option that is enabled (in YES state) and we want him to disable it - according to this we set our record of his state to WANTNO {and send him the DONT command} - but this has moved our record of the state away from the YES state and the guide suggests that the option is now to be considered disabled even though we have not yet got a WONT to confirm it - fair enough he MUST accept it as the disabled state corresponding to the vanilla NVT requires that - but surely it does not take effect until we get the acknowledgement?

0 Answers
Related