Need a sample/demo of using TIdTelnet to interact with telnet server
Asked Answered
L

3

5

I tried to employ Indy 10.5.5 (shipped with Delphi 2010) for:

  • connecting to telnet server
  • performing username/password authentication (gaining access to the command shell)
  • executing a command with returning resulting data back to application

and had no success, additionally i'm completely lost in spaghetti logic of Indy's internals and now have no idea why it didnt work or how i supposed to send strings to the server and grab the results. Need some sample code to study.


Formal form of the question: Where can i get 3-rd party contributed demo covering TIdTelnet component? (indyproject.org demos webpage do not have one)

Larisa answered 18/6, 2011 at 14:10 Comment(6)
No, it is not taboo. Post your actual problem instead of just asking for a working example. Did you try google?Davenport
Many other questions get answered by referring to the documentation. Why is everyone trying to close a question that is asking for an example application on how to do something.Punchball
Well, there are two possible situations (a) there is a sample app available - in this case an answer with a link is possible but google will do much better than SO here (b) there is no such sample, in this case the question is way to broad. And the formulation "and had no success" screams for further explanations.Davenport
@Robert Love, thats an essence of this question (i admit, i failed to figure out from documentation myself)Larisa
@Downvoter I can see the others points. Instead of asking for a Demo. Ask "Using TidTelent how do I connect to server, send a command and receive a response?" Avoid comments like "Spaghetti logic"Punchball
Two minutes with Google found this link that may help.Pass
P
5

The main problem with Telnet is that it DOES NOT utilize a command/response model like most other Internet protocols do. Either party can send data at any time, and each direction of data is independant from the other direction. This is reflected in TIdTelnet by the fact that it runs an internal reading thread to receive data. Because of this, you cannot simply connect, send a command, and wait for a response in a single block of code like you can with other Indy components. You have to write the command, then wait for the OnDataAvailable event to fire, and then parse the data to determine what it actually is (and be prepared to handle situations where partial data may be received, since that is just how TCP/IP works).

If you are connecting to a server that actually implements a command/response model, then you are better off using TIdTCPClient directly instead of TIdTelnet (and then implement any Telnet sequence decoding manually if the server really is using Telnet, which is rare nowadays but not impossible). For Indy 11, we might refactor TIdTelnet's logic to support a non-threaded version, but that is undecided yet.

Proceed answered 20/6, 2011 at 9:21 Comment(2)
Hey @RemyLebeau. :-) I would like to know, if you made a decision to add support for a non-threaded version of TIdTelnet? But I guess not, since it is 2018 and I still don't see any possibility in my current version (10.1). What I would like to have (instead of a completely alternate TIdTelnet version) is an additional possibility to use a non-threaded command <-> response function. I often need both options simultaniously (server sends status msgs + I need response for certain commands). I think it would be much harder to write everything from ground up myself based on TIdTCPClient... :-/Gaut
@Gaut Indy 11 is not what I originally envisioned for it. It is going to be a maintenance release to drop support for old compilers and clean up the current code (I should write a blog about that). Bigger changes (new classes, redesigns, etc) are being pushed back to Indy 12. But there are currently no plans for a non-threaded TIdTelnet. Supporting unsolicited status messages in the middle of a command-response protocol is always tricky, it needs to be part of the protocol design itself. That is not something that Indy could provide a generic wrapper for, as it is very protocol-specific.Proceed
D
3

done with indy. no comments.. just som old code :-) telnet don't like the send string kommand.. use sendch.

telnetdude.Host := 1.1.1.1;
try
telnetdude.connect;
except
on E: Exception do begin
E.CleanupInstance;
end; {except}
if telnetdude.Connected then begin
for i := 1 to length(StringToSend) do telnetdude.sendch(StringToSend[i]);
telnetdude.sendch(#13);
end;
end; {while}
end; {if}
if telnetdude.Connected then telnetdude.Disconnect;
end;
Dinesh answered 20/1, 2012 at 17:1 Comment(1)
Why was this upvoted? I don't really understand how this code relates to the question. It basically does just the same what the SendString() function does. And it gives no possibility to retrieve any response (especially to a certain command). Or do I miss something here?Gaut
K
1

I hope this helps anyone looking for answers to a similar question.

Firstly, It would seem the typical command/response model (as mentioned above, does indeed NOT apply).

So I just got it working for some very simple application (rebooting my router).

Specific additions to above code from Johnny Lanewood (and perhaps some clarification) a) You have to send #13 to confirm the command b) I got "hangs" on every command I sent / response I requested UNTIL I enabled ThreadedEvent. (this was my big issue)

c) the OnDataAvailable event tells you when new data is available from the Telnet Server - however there are no guarantees as to what this data is - i.e. it's pretty what you get in the command line / what ever is appended to the previous responses. But is is NOT a specific response line to your command - it's whatever the telnet server returns (could be welcome info, ASCII drawings etc etc.)

Given (c) above, one would rather check the OnDataAvailable event and parse the data (knowing what you'd expect). When the output stops (i.e. you need build a mechanism for this), you can parse the data and determine whether the server is ready for something new from the client. For the purpose of my code below, I set a read timemout and I just used Sleep(2000) - ignorantly expecting no errors and that the server would be ready after the sleep for the next command. My biggest stumbling block was ThreadedEvent := True (see above in b)

Thus, my working solution (for specific application, and possibly horrible to some).

   lIDTelnet := TIdTelnet.Create(nil);
    try
      lIdTelnet.ReadTimeout := 30000;
      lIDTelnet.OnDataAvailable := TDummy.Response;
      lIDTelnet.OnStatus := TDummy.Status;
      lIdTelnet.ThreadedEvent := True;

      try
        lIDTelnet.Connect('192.168.0.1', 23);
        if not lIDTelnet.Connected then
          Raise Exception.Create('192.168.0.1 TELNET Connection Failed');

        Sleep(2000);

        lIdtelnet.SendString(cst_user + #13);
        Sleep(2000);

        lIdtelnet.SendString(cst_pass + #13);
        Sleep(2000);

        lIdtelnet.SendString(cst_reboot + #13);
        Sleep(2000);

        if lIDTelnet.Connected then
           lIDTelnet.Disconnect;
      except
        //Do some handling
      end;
  finally 
    FreeAndNil(lIdTelnet);
  end;

and then

class procedure TDummy.Response(Sender: TIdTelnet; const Buffer: TIdBytes);
begin
  Write(TDummy.ByteToString(Buffer));
end;

class function TDummy.ByteToString(
  const aBytes: TIdBytes): String;
var
  i : integer;
begin
  result := '';
  for i := 0 to Length(aBytes) -1 do
  begin
    result := result + Char(aBytes[i]);
  end;
end;
Knout answered 24/5, 2020 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.