rfc1143.rb 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. require_relative 'telnet/codes'
  2. # rfc1143 state machine for telnet protocol
  3. class RFC1143
  4. include Telnet::Codes
  5. attr_reader :telopt
  6. attr_reader :us
  7. attr_reader :him
  8. attr_reader :agree
  9. def initialize(to, u, h, a)
  10. @telopt = to
  11. @us = u
  12. @him = h
  13. @agree = a
  14. end
  15. =begin
  16. EXAMPLE STATE MACHINE
  17. FOR THE Q METHOD OF IMPLEMENTING TELNET OPTION NEGOTIATION
  18. There are two sides, we (us) and he (him). We keep four
  19. variables:
  20. us: state of option on our side (NO/WANTNO/WANTYES/YES)
  21. usq: a queue bit (EMPTY/OPPOSITE) if us is WANTNO or WANTYES
  22. him: state of option on his side
  23. himq: a queue bit if him is WANTNO or WANTYES
  24. An option is enabled if and only if its state is YES. Note that
  25. us/usq and him/himq could be combined into two six-choice states.
  26. "Error" below means that producing diagnostic information may be a
  27. good idea, though it isn't required.
  28. Upon receipt of WILL, we choose based upon him and himq:
  29. NO If we agree that he should enable, him=YES, send
  30. DO; otherwise, send DONT.
  31. YES Ignore.
  32. WANTNO EMPTY Error: DONT answered by WILL. him=NO.
  33. OPPOSITE Error: DONT answered by WILL. him=YES*,
  34. himq=EMPTY.
  35. WANTYES EMPTY him=YES.
  36. OPPOSITE him=WANTNO, himq=EMPTY, send DONT.
  37. * This behavior is debatable; DONT will never be answered by WILL
  38. over a reliable connection between TELNETs compliant with this
  39. RFC, so this was chosen (1) not to generate further messages,
  40. because if we know we're dealing with a noncompliant TELNET we
  41. shouldn't trust it to be sensible; (2) to empty the queue
  42. sensibly.
  43. =end
  44. def handle_will
  45. case @us
  46. when :no
  47. if @agree
  48. return TELNET_DO, @telopt
  49. else
  50. return TELNET_DONT, @telopt
  51. end
  52. when :yes
  53. # ignore
  54. return nil, nil
  55. when :wantno
  56. @him = :no
  57. return :error, "DONT answered by WILL"
  58. when :wantno_opposite
  59. @him = :yes
  60. return :error, "DONT answered by WILL"
  61. when :wantyes
  62. @him = :yes
  63. return nil, nil
  64. when :wantyes_opposite
  65. @him = :wantno
  66. return TELNET_DONT, @telopt
  67. end
  68. end
  69. =begin
  70. Upon receipt of WONT, we choose based upon him and himq:
  71. NO Ignore.
  72. YES him=NO, send DONT.
  73. WANTNO EMPTY him=NO.
  74. OPPOSITE him=WANTYES, himq=NONE, send DO.
  75. WANTYES EMPTY him=NO.*
  76. OPPOSITE him=NO, himq=NONE.**
  77. * Here is the only spot a length-two queue could be useful; after
  78. a WILL negotiation was refused, a queue of WONT WILL would mean
  79. to request the option again. This seems of too little utility
  80. and too much potential waste; there is little chance that the
  81. other side will change its mind immediately.
  82. ** Here we don't have to generate another request because we've
  83. been "refused into" the correct state anyway.
  84. =end
  85. def handle_wont
  86. case @us
  87. when :no
  88. return nil, nil
  89. when :yes
  90. @him = :no
  91. return TELNET_DONT, @telopt
  92. when :wantno
  93. @him = :no
  94. return nil, nil
  95. when :wantno_opposite
  96. @him = :wantyes
  97. return TELNET_DO, @telopt
  98. when :wantyes
  99. @him = :no
  100. return nil, nil
  101. when :wantyes_opposite
  102. @him = :no
  103. return nil, nil
  104. end
  105. end
  106. =begin
  107. If we decide to ask him to enable:
  108. NO him=WANTYES, send DO.
  109. YES Error: Already enabled.
  110. WANTNO EMPTY If we are queueing requests, himq=OPPOSITE;
  111. otherwise, Error: Cannot initiate new request
  112. in the middle of negotiation.
  113. OPPOSITE Error: Already queued an enable request.
  114. WANTYES EMPTY Error: Already negotiating for enable.
  115. OPPOSITE himq=EMPTY.
  116. We handle the option on our side by the same procedures, with DO-
  117. WILL, DONT-WONT, him-us, himq-usq swapped.
  118. =end
  119. def handle_do
  120. case @him
  121. when :no
  122. @us = :wantyes
  123. return TELNET_WILL, @telopt
  124. when :yes
  125. return :error, 'Already enabled'
  126. when :wantno
  127. # us = :wantno_opposite # only if "buffering", whatever that means.
  128. return :error, 'Request in the middle of negotiation'
  129. when :wantno_opposite
  130. return :error, 'Already queued request'
  131. when :wantyes
  132. return :error, 'Already negotiating for enable'
  133. when :wantyes_opposite
  134. @us = :wantyes
  135. return nil, nil
  136. end
  137. end
  138. =begin
  139. If we decide to ask him to disable:
  140. NO Error: Already disabled.
  141. YES him=WANTNO, send DONT.
  142. WANTNO EMPTY Error: Already negotiating for disable.
  143. OPPOSITE himq=EMPTY.
  144. WANTYES EMPTY If we are queueing requests, himq=OPPOSITE;
  145. otherwise, Error: Cannot initiate new request
  146. in the middle of negotiation.
  147. OPPOSITE Error: Already queued a disable request.
  148. We handle the option on our side by the same procedures, with DO-
  149. WILL, DONT-WONT, him-us, himq-usq swapped.
  150. =end
  151. def handle_dont
  152. case @him
  153. when :no
  154. return :error, 'Already disabled'
  155. when :yes
  156. @us = :wantno
  157. return TELNET_WONT, @telopt
  158. when :wantno
  159. return :error, 'Already negotiating for disable'
  160. when :wantno_opposite
  161. @us = :wantno
  162. return nil, nil
  163. when :wantyes
  164. # us = :wantno_opposite # only if "buffering", whatever that means.
  165. return :error, 'Request in the middle of negotiation'
  166. when :wantyes_opposite
  167. return :error, 'Already queued disable request'
  168. end
  169. end
  170. end