Как подавить эхо с помощью TIdTelnet?

Использование Indy10 - DelphiXE. Как мне сообщить серверу telnet, к которому я подключаюсь с помощью TIdTelnet, чтобы он не повторял команды, которые я отправляю на сервер telnet?

Это моя текущая попытка (которая не работает), я пытаюсь отправить последовательность IAC DO SUPPRESS_LOCAL_ECHO при каждой записи telnet, но я считаю, что это нужно делать на этапе согласования?

uses
  TIdTelnet,

...
procedure TIOTelnetConnection.SendSupressEcho;

var
  Resp: TIdBytes;

begin
 SetLength(Resp, 3);
 Resp[0] := TNC_IAC;
 Resp[1] := TNC_DO;
 Resp[2] := TNO_SUPLOCALECHO;
 FIOConnection.IOHandler.Write(Resp);
end;

procedure TIOTelnetConnection.Write(Str: AnsiString);
begin
 SendSupressEcho;
 if Str <> '' then
  FIOConnection.IOHandler.Write(Str);
end;

глядя на исходный код TIdTelnet, я вижу процедуру Negotiate, но она защищена, как я могу переопределить ее поведение?


person whosrdaddy    schedule 20.06.2013    source источник


Ответы (3)


Если сервер отправляет вам IAC WILL ECHO, TIdTelnet жестко закодирован для отправки ответа IAC DO ECHO, а затем запускает событие OnTelnetCommand(tncNoLocalEcho), чтобы запретить локальное эхо того, что вы отправляете.

Если сервер отправляет вам IAC WONT ECHO, TIdTelnet жестко запрограммирован на отправку ответа IAC DONT ECHO, а затем запускает событие OnTelnetCommand(tncLocalEcho), сообщая вам о локальном эхо того, что вы отправляете.

Если сервер отправляет вам IAC DO ECHO, TIdTelnet жестко закодирован для отправки ответа IAC WILL ECHO, а затем запускает событие OnTelnetCommand(tncEcho), чтобы сообщить вам, что вы должны повторить все, что вы получаете.

Если сервер отправляет вам IAC DONT ECHO, TIdTelnet жестко закодирован для отправки ответа IAC WONT ECHO, а затем запускает событие OnTelnetCommand(tncLocalEcho), чтобы сообщить вам, что вы должны повторить все, что вы получаете.

Итак, если вы не хотите, чтобы сервер отвечал вам эхом, вы можете отправить серверу команду IAC DONT ECHO, а не команду IAC DO SUPLOCALECHO. Затем сервер ответит либо IAC WONT ECHO, либо IAC WILL ECHO соответственно (на что, по-видимому, затем ответит TIdTelnet, но сервер не ответит снова, поскольку его ECHO текущее состояние не изменится, избегая бесконечного цикла ответов).

person Remy Lebeau    schedule 20.06.2013
comment
Спасибо, я нашел это немного запутанным, но действительно IAC DONT ECHO — это последовательность, которую я искал. Есть ли шанс изменить это свойство в более поздних версиях Indy? - person whosrdaddy; 20.06.2013
comment
TIdTelnet очень примитивен, поэтому я уверен, что многое можно сделать, чтобы улучшить его в будущей версии. - person Remy Lebeau; 21.06.2013

@Remy Lebeau: Насколько я понимаю, вы говорите: если сервер отправляет вам IAC DO ECHO, TIdTelnet жестко закодирован для отправки ответа IAC WILL ECHO, а затем запускает OnTelnetCommand( tncEcho ), чтобы сообщить вам, что нужно возвращать все, что вы получаете.

Это имеет смысл и согласуется с целью RFC857...

Однако в коде мы имеем:

procedure TIdTelnet.Negotiate;
...
      TNC_DONT:
        begin
          b := IOHandler.ReadByte;
          case b of
            TNO_ECHO:
              begin
                DoTelnetCommand(tncEcho);
                //DoStatus('ECHO');    {Do not Localize}
                Reply := TNC_WONT;
              end;
            else
              Reply := TNC_WONT;
          end;

а также

  TNC_DO:
    begin
      b := IOHandler.ReadByte;
      case b of
        TNO_ECHO:
          begin
            Reply := TNC_WILL;
            DoTelnetCommand(tncLocalEcho);
          end;

Наверняка этот код неверный? (в версии 10.6.0.497 Indy)

Я считаю, что это будет иметь больше смысла:

 procedure TIdTelnet.Negotiate;
 ...
      TNC_DONT:
         begin
           b := IOHandler.ReadByte;
           case b of
             TNO_ECHO:
               begin
               // Agree not to echo back everything received from server 
               // (This being the default - you shouldn't echo unless asked to)
                  Reply := TNC_WONT;            
               // Therefore only print locally what is sent to server
               // (Again: this is the default behavior without negotiation) 
                  DoTelnetCommand(tncLocalEcho);
               end;
             else
               Reply := TNC_WONT;
           end;

а также

   TNC_DO:
     begin
       b := IOHandler.ReadByte;
       case b of
         TNO_ECHO:
           begin
           // Agree to echo back everything received from server 
              Reply := TNC_WILL;
              DoTelnetCommand(tncEcho); 
           // Therefore you may still have to locally print what you send
           // (i.e. local echo is usually still implicit in this)
           end;   

Другими словами, я считаю, что код в настоящее время изменен с того, каким он должен быть, а именно, что сервер, отправляющий DO ECHO, должен порождать токен tncEcho - это то, что вы сказали в приведенной выше цитате!

Как этот баг продержался так долго? (вероятно, потому что большинство серверов Telnet больше не утруждают себя согласованием эха RFC857)

К сожалению, на данный момент я вижу единственный способ «компенсировать» эту ошибку — создать копию файла IDTelnet.pas; свяжите это с вашим проектом в менеджере проектов; а затем внесите исправления в эту копию, как описано выше.

person Alex T    schedule 14.08.2013

Чтобы решить исходный вопрос: «Как подавить эхо с помощью TIdTelnet?», я бы предложил следующие шаги:

  1. Добавьте TIdConnectionIntercept в свою форму и подключите его к компоненту TIdTelnet через свойство Intercept с помощью инспектора объектов.

  2. Создайте обработчик события TIdConnectionIntercept OnSend.

  3. Используйте это, чтобы определить, когда его параметр var ABuffer: TIdBytes получает ответ на команду согласования сервера относительно эха. Обратите внимание, что буфер БУДЕТ содержать ТОЛЬКО и ТОЛЬКО все три байта ответа из-за того, как ответ заполняется и отправляется TIdTelnet.Negotiate

  4. Измените средний байт по желанию (например, чтобы принудительно изменить WILL на WONT или DO на DONT в соответствии с тем, что вы пытаетесь применить).

Иов "хороший человек"

Помните, конечно, что этот метод вводит изменение ПОСЛЕ вызова TidTelnet OnTelnetCommand, поэтому вам придется изменить все, что вы настроили в этом обработчике, чтобы он соответствовал вашему измененному ответу.

person Alex T    schedule 14.08.2013
comment
К сожалению, с тех пор я обнаружил, что принудительное DONT в клиенте приведет к тому, что СЕРВЕР, написанный на Delphi XE4, вызовет исключение утверждения в IOHandler! По-видимому, это связано с тем, что жесткое кодирование в TidTelnet создает дополнительный ответ: т. е. мы получаем последовательность БУДЕТ -> НЕ ДОЛЖЕН -> НЕ БУДЕТ -> НЕ ДОЛЖЕН происходить (и последнее НЕ должно происходить). Неожиданная 3-байтовая команда может привести к тому, что сервер (при написании с включенной поддержкой Unicode) обнаружит отсутствующий байт и вызовет исключение утверждения. - person Alex T; 21.08.2013
comment
Итак, подведем итог: (а) TidTelnet будет слишком часто отвечать в последовательности БУДЕТ-НЕ-БУДЕТ и (б) TidTelnetServer может иметь ошибку, связанную с получением неожиданных последовательностей байтов с нечетными номерами. - person Alex T; 21.08.2013
comment
Возможно, мне пришлось использовать результат := AContext.Connection.IoHandler.WaitFor(chr(TNC_IAC),False,False,Indy8BitEncoding,Timeout); в коде сервера, так как не было перегрузки TidByte для WaitFor. Это упущение перегрузки может быть в основе проблемы, хотя я все еще не думаю, что это очень хорошо, если неожиданные входящие данные вызывают сбой сервера и его отключение! - person Alex T; 21.08.2013
comment
То же самое происходит при внедрении WONT: т. е. мы получаем последовательность DO-WONT-DONT-WONT, где последнее WONT вызывает исключение утверждения на сервере. Отключение утверждений останавливает сбой, но сервер получает два неожиданных символа (последние 2 байта 3-байтового WONT), смешанные с обычным текстовым вводом. Так что мне кажется, что поддержка юникода и Telnet еще не доработаны должным образом. - person Alex T; 21.08.2013
comment
На данный момент я решил проблему, обнаружив ненужную повторную передачу 3-байтовых ответов WONT и DONT в обработчике TidConnectionIntercept OnSend и установив для каждого три нуля (0,0,0). Это не позволяет серверу вызвать ошибку утверждения, поскольку он не пытается интерпретировать непредвиденную команду. Отправка трех нулей — довольно безопасная (хотя и раздражающая) вещь. К сожалению, кажется, что вы не можете использовать OnSend, чтобы просто удалить буфер. - person Alex T; 21.08.2013