Check remote port access using Delphi - Telnet style - delphi

I deploy my application in environments heavily stricken with firewalls. Frequently I find myself using Telnet to check if a port is open and accessible in the network.
Now I would like to implement an equivalent functionality of the command, Telnet [domainname or ip] [port], in Delphi.
Is it adequate that I just attempt to open and close a TCP/IP socket without sending or receiving any data?
Is there any risk that I might crash the arbitrary application/service listening on the other end?
Here's my code:
function IsPortActive(AHost : string; APort : Word):boolean;
var IdTCPClient : TIdTCPClient;
begin
IdTCPClient := TIdTCPClient.Create(nil);
try
try
IdTCPClient.Host := AHost;
IdTCPClient.Port := APort;
IdTCPClient.Connect;
except
//Igonre exceptions
end;
finally
result := IdTCPClient.Connected;
IdTCPClient.Disconnect;
FreeAndNil(IdTCPClient);
end;
end;

If you just want to check whether the port is open, then you can use this:
function IsPortActive(AHost : string; APort : Word): boolean;
var
IdTCPClient : TIdTCPClient;
begin
Result := False;
try
IdTCPClient := TIdTCPClient.Create(nil);
try
IdTCPClient.Host := AHost;
IdTCPClient.Port := APort;
IdTCPClient.Connect;
Result := True;
finally
IdTCPClient.Free;
end;
except
//Ignore exceptions
end;
end;
But that only tells you if any server app has opened the port. If you want to make sure that YOUR server app opened the port, then you will have to actually communicate with the server and make sure its responses are what you are expecting. For this reason, many common server protocols provide an initial greeting so clients can identify the type of server they are connected to. You might consider adding a similar greeting to your server, if you are at liberty to make changes to your communication protocol.
Simply opening a connection to the server does not impose any risk of crashing the server, all it does is momentarily occupy a slot in the server's client list. However, if you actually send data to the server, and the server app you are connected to is not your app, then you do run a small risk if the server cannot handle arbitrary data that does not conform it its expected protocol. But that is pretty rare. Sending a small command is not uncommon and usually pretty safe, you will either get back a reply (which may be in a format that does not conform to your protocol, so just assume failure), or you may not get any reply at all (like if the server is waiting for more data, or simply is not designed to return a reply) in which case you can simply time out the reading and assume failure.

Related

Indy IdFTP fails on Active connection

I'm trying to use Indy's IdFTP to send and receive some files via FTP.
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
var
TimeoutFTP: integer;
begin
Result := false;
with TIdFTP.Create(Fm) do
try
try
TimeoutFTP := 2000;
Host := Servidor;
Port := 21;
UserName := Usuario;
PassWord := Contra;
Passive := Pasivo;
Connect(true, TimeoutFTP);
if not Connected then
begin
Error := true;
end
else
begin
TransferType := ftASCII;
if Binario then
TransferType := ftBinary;
OnWorkEnd := FinDeTransmision;
if Descargar then
Get(Remoto , Local, True)
else
Put(InterpretarRutaEspecial(Local), Remoto, True);
if Descargar and Borrar then
Delete(Remoto);
Disconnect;
Result := true;
Fm.Hide;
end;
Except on E: Exception do
Mensaje := E.Message;
end;
finally
Free;
end;
if not Result then
ErrorTransmision;
end;
Whenever I try to do a PUT/GET on Active mode I get the following error: EIdProtocolReplyError: 'Failed to establish connection". It works fine on Passive mode.
The thing is that I want to use Indy (used elsewhere in the project) but the previous version of the code, using OverbyteIcsFtpCli works fine both in Active and Passive mode.
This is the code using OverbyteIcsFtpCli:
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
begin
with TFtpClient.Create(Fm) do
try
HostName := Servidor;
Port := '21';
UserName := Usuario;
PassWord := Contra;
HostDirName := '';
HostFileName := Origen;
LocalFileName := InterpretarRutaEspecial(Destino);
Binary := Binario;
Passive := Pasivo;
OnRequestDone := FinDeTransmision;
if Descargar then
Result := Receive
else
Result := Transmit;
OnRequestDone := nil;
if Descargar and Borrar then
Delete;
Result := Result and not Error;
Fm.Hide;
if not Result then
ErrorTransmision;
finally
Free;
end;
end;
So I took a look under the hood using wireshark and I found that Indy's FTP is not answering some messages from the server.
This is the file-transmission handshake with OverBytes' FTP:
I've highlighted in yellow the two packets sent between server and client that start the data transmission.
Now let's see what happens with Indy's FTP:
The server is sending the packet to start the file transmission but IdFTP is not answering.
I've seen this question but this two tests where ran in the same computer, same network connection, same firewall, etc. Also this one, but I want the FTP to work both in active and passive modes.
What's happening?
In an Active mode transfer, an FTP server creates an outgoing TCP connection to the receiver.
Your Wireshark captures clearly show that the FTP server in question is creating that transfer connection BEFORE sending a response to the RETR command to let your client know that the connection is proceeding. TFtpClient is accepting that connection before receiving the RETR response. But TIdFTP waits for the RETR response before it will then accept the transfer connection (this also applies to TIdFTP's handling of STOR/STOU/APPE commands, too).
LPortSv.BeginListen; // <-- opens a listening port for transfer
...
SendPort(LPortSv.Binding); // <-- sends the PORT command
...
SendCmd(ACommand, [125, 150, 154]); // <-- sends the RETR command and waits for a response!
...
LPortSv.Listen(ListenTimeout); // <-- accepts the transfer connection
...
Re-reading RFC 959, it says the following:
The passive data transfer process (this may be a user-DTP or a second server-DTP) shall "listen" on the data port prior to sending a transfer request command. The FTP request command determines the direction of the data transfer. The server, upon receiving the transfer request, will initiate the data connection to the port. When the connection is established, the data transfer begins between DTP's, and the server-PI sends a confirming reply to the user-PI.
ICS is asynchronous, so this situation is not a big deal for it to handle. But Indy uses blocking sockets, so TIdFTP will need to be updated to account for this situation, likely by monitoring both command and transfer ports simultaneously so it can act accordingly regardless of the order in which the transfer connection and the command response arrive in.
I have opened a ticket in Indy's issue tracker for this:
#300: TIdFTP fails on Active mode transfer connection with vsFTPd
UPDATE: the fix has been merged into the main code now.

Opening a Firebird database file on a network share

I thought converting a mapped drive letter to a UNC path would be enough to be able to open a .GDB file,
but alas:
function ConvertToUNCPath(AMappedDrive: string) : string;
var
lRemoteString : array[0..255] of char;
lpRemote : PChar;
lStringLen : Cardinal;
begin
lpRemote := #lRemoteString;
lStringLen := 255;
If WNetGetConnection(Pchar(ExtractFileDrive(AMappedDrive)) ,
lpRemote,
lStringLen) = NO_ERROR Then
Result := lRemoteString
else
Result := ''; // No mapping found
end;
function TDataModuleData.OpenGDBDatabase(AGDBName: string) : Boolean;
var
lDlgLogin: TFrmLogin;
p : Integer;
lUNC,
lErrMsg : String;
begin
Result := False;
with FDConnection do // TFDConnection
begin
Close;
TxOptions.Isolation := xiDirtyRead;
p := Pos(':',AGDBName);
if p = 2 then
begin
lUNC := ConvertToUNCPath(Copy(AGDBName,1,2));
if lUNC <> '' then
begin
lUNC := Copy(lUNC,3);
p := pos('\',lUNC);
AGDBName := Copy(lUNC,p) + Copy(AGDBName,3);
lUNC := copy(lUNC,1,p-1);
end;
end;
DriverName := S_FD_IBId;
Params.Database := AGDBName;
if lUNC <> '' then
Params.Add('Server=' + lUNC)
else
Params.Add('Server=localhost'); // Not strictly necessary
Params.UserName := 'SYSDBA';
Params.Password := 'masterkey';
try
Open;
Result := Connected;
except
on E:Exception do
begin
lErrMsg := LowerCase(E.Message);
end;
end;
end;
end;
Depending on how I parse the ConvertToUNCPath result I get different error messages:
[firedac][phys][ib]unavailable database
[firedac][phys][ib]i/o error during "createfile (open)" operation for file "persoonlijk\jan\klanten.gdb"'#$D#$A'error while trying to open file'#$D#$A'the system cannot find the path specified.
The part of the code using ConvertToUNCPath succesfully converts e.g. P:\Jan\KLANTEN.GDB to \\tt2012server\persoonlijk\Jan\KLANTEN.GDB.
How can I open a GDB file when the path points to a mapped drive letter?
Added: I tried these hardcoded variations, they all fail:
// lUNC := '\\2012server'; // Unable to complete network request to host
lUNC := 'tt2012server';
//AGDBName := '\\tt2012server\persoonlijk\jan\klanten.gdb';
//AGDBName := 'tt2012server\persoonlijk\jan\klanten.gdb';
//AGDBName := '\persoonlijk\jan\klanten.gdb';
//AGDBName := 'persoonlijk\jan\klanten.gdb';
//AGDBName := '\jan\klanten.gdb';
//AGDBName := 'jan\klanten.gdb';
//AGDBName := 'p:\jan\klanten.gdb'; (original input)
(P: maps to \\tt2012server\persoonlijk)
Added:
Sorry, I was not clear in my initial text: this is not about connecting to a database on a remote server per se. I just want my local 'DB inspection' tool to be able to open a GDB file if someone places it in my network share for inspection (instead of having to copy it to local disk first).
To only intention of using WNetGetConnection was to resolve drive letter to UNC path (some I code I found on the web).
1. Firebird explicitly denies attempts to open database files on non-local disks
Firebird is database server, and as such it focuses on performance and reliability.
http://www.firebirdfaq.org/faq46/
Performance means lots of data is cached, both cached for reading and cached for writing.
Reliability means Firebird has to gain worthy warrants from OS that:
a. no other process would tinker with the database file while the server has some data from it cached for reading.
b. at any moment in time the server might wish to write any data to the file from its cache and it is warranted that that data - at any moment in time - ends persistently written to the persistent media.
Network-connected disks nullify both warranties and consequently Firebird Server refuses to trust them.
You may hack Firebird configuration or source files on your own discretion to remove this safety check and open network-shared files, if you really need this more than safety and speed.
But proper solution would be installing Firebird server on the machine whose disks do carry the database file.
2. Connection String is not a database file name
AGDBName := '\\tt2012server\persoonlijk\jan\klanten.gdb'
This does NOT mean "local Firebird server should connect to tt2012server server using LOCAL_SYSTEM credentials and read the database file from persoonlijk shared resource", as you probably intended it to mean.
http://www.firebirdfaq.org/faq260/
If anything, Windows LOCAL_SYSTEM user is explicitly barred from most network operations to contain intruders and viruses. Even if you hack Firebird into opening network files, most probably Windows would prohibit this access anyway, unless you would setup your Windows to run Firebird Server service with some user account other than the default LOCAL_SYSTEM.
Anyway, what \\tt2012server\persoonlijk\jan\klanten.gdb Connection String actually means is that you request your application to connect to tt2012server using WNET (aka Microsoft Named Pipes) protocol and find Firebird server running on that server and communicating by WNET protocol, as opposed to TCP/IP protocol.
Judging by the error you quote - lUNC := '\\2012server'; // Unable to complete network request to host - the said tt2012server computer perhaps does not have a Firebird Server running and accepting Named Pipes connections.
The WNET protocol is considered obsoleted and would most probably be removed from the future Firebird Server versions. As of now it is working, but few people use it, thus little up to date experience exists in that area. It is suggested you would use TCP/IP protocol by default to connect your application to the Firebird Server running on the tt2012server machine, not WNET protocol.
PS. This question has duplicates:
Connecting to Firebird database from Windows local network
ibase_connect: remote computer host and shared db file from windows
PPS. Firebird is a multi-generation database engine.
Consequently, there is no "dirty read" transactions possible in Interbase/Yaffil/Firebird family.
TxOptions.Isolation := xiDirtyRead; - this line would not work. Most probably it would silently change the transaction class to "READ COMMITTED", less probably it would give an explicit error.

IP Black List TcpServer

How i can limit the connections per ip? i've tried this code how to block unknown clients in indy (Delphi) but if my server is flooded i can't connect. the code from Remy just prevent CPU to use 100% and use more ram. but the connection from flood still alive on tcpserver and i can't connect to server. so my question is, how i can limit the connection before onconnect, something on accept using tcpserver? maybe hook accept function and try limit the connection ?
How i can limit the connections per ip?
You already know the answer to that, as that is exactly what the other code is doing.
if my server is flooded i can't connect.
The purpose of the other code is simply to limit a client IP address to a max of 10 simultaneous connections, not to prevent flooding or lower CPU/RAM usage. You can't stop unwanted clients from connecting to your server, unless you deactivate the server, or set its MaxConnections property. Outside of that, about all you can do is disconnect unwanted clients as soon as possible, which you can do in the server's OnConnect event. But if you are getting flooded, that is going to take time to process, especially if you are continuously locking and unlocking the server's Contexts list, which will end up serializing the server's internal threading.
Flood management really needs to be handled by a firewall or router/load balancer, not in the server app itself. If this is not acceptable to you, then at least on Windows only, an option might be to write a custom TIdServerIOHandlerStack-derived component that overrides the virtual Accept() method to call WinSock's WSAAccept() function, which offers a callback you can use to reject connections before they leave the accept queue, and thus they will not be seen by TIdTCPServer. For example:
type
TMyServerIOHandler = class(TIdServerIOHandlerStack)
public
function Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; override;
end;
function MyConditionFunc(lpCallerId, lpCallerData: LPWSABUF; lpSQOS, lpGQOS: LPQOS; lpCalleeId, lpCalleeData: LPWSABUF; g: PGROUP dwCallbackData: DWORD_PTR): Integer; stdcall;
begin
if (the address stored in lpCallerId is blocked) then
Result := CF_REJECT
else
Result := CF_ACCEPT;
end;
type
TIdSocketHandleAccess = class(TIdSocketHandle)
end;
function TMyServerIOHandler.Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler;
var
LIOHandler: TIdIOHandlerSocket;
LBinding: TIdSocketHandle;
LAcceptedSocket: TIdStackSocketHandle;
begin
Result := nil;
LIOHandler := TIdIOHandlerStack.Create(nil);
try
LIOHandler.Open;
while not AListenerThread.Stopped do
begin
if ASocket.Select(250) then
begin
LBinding := LIOHandler.Binding;
LBinding.Reset;
LAcceptedSocket := WSAAccept(ASocket.Handle, nil, nil, #MyConditionFunc, 0);
if LAcceptedSocket <> Id_INVALID_SOCKET then
begin
TIdSocketHandleAccess(LBinding).SetHandle(LAcceptedSocket);
LBinding.UpdateBindingLocal;
LBinding.UpdateBindingPeer;
LIOHandler.AfterAccept;
Result := LIOHandler;
LIOHandler := nil;
Break;
end;
end;
end;
finally
FreeAndNil(LIOHandler);
end;
end;
Then you can assign an instance of TMyServerIOHandler to the TIdTCPServer.IOHandler property before activating the server.

Start Communication from server first in delphi by Indy 10

In Socket applications programmed by TCPServer/Client components, usually we active server side, then connect client to server, and when we need to get or send data from one side to other, first we send a command from client to server and a communication will starts.
But the problem is that always we need to start conversation from client side!
I want to ask is any idea for start conversation randomly from server side without client side request?
I need this functionality for notify client(s) from server side. for example, when a registered user (client-side) connected to server, other connected users (on other client-sides), a notification must send from server to all users (like Yahoo Messenger).
I'm using TIdCmdTCPServer and TIdTCPClient components
You are using TIdCmdTCPServer. By definition, it sends responses to client-issued commands. For what you are asking, you should use TIdTCPServer instead, then you can do whatever you want in the TIdTCPServer.OnExecute event.
What you ask for is doable, but its implementation depends on your particular needs for your protocol.
If you just want to send unsolicited server-to-client messages, and never responses to client-to-server commands, then the implementation is fairly straight forward. Use TIdContext.Connection.IOHandler when needed. You can loop through existing clients in the TIdTCPServer.Contexts list, such as inside the TIdTCPServer.OnConnect and TIdTCPServer.OnDisconnect events. On the client side, you need a timer or thread to check for server messages periodically. Look at TIdCmdTCPClient and TIdTelnet for examples of that.
But if you need to mix both client-to-server commands and unsolicited server-to-client messages on the same connection, you have to design your protocol to work asynchronously, which makes the implementation more complex. Unsolicited server messages can appear at anytime, even before the response to a client command. Commands need to include a value that is echoed in the response so clients can match up responses, and the packets need to be able to differentiate between a response and an unsolicited message. You also have to give each client its own outbound queue on the server side. You can use the TIdContext.Data property for that. You can then add server messages to the queue when needed, and have the OnExecute event send the queue periodically when it is not doing anything else. You still need a timer/thread on the client side, and it needs to handle both responses to client commands and unsolicited server messages, so you can't use TIdConnection.SendCmd() or related methods, as it won't know what it will end up reading.
I have posted examples of both approaches in the Embarcadero and Indy forums many times before.
Clients initiate communication. That is the definition of a client–the actor that initiates the communication. Once the connection is established though, both sides can send data. So, the clients connect to the server. The server maintains a list of all connected clients. When the server wants to send out communications it just sends the data to all connected clients.
Since clients initiate communication, it follows that, in the event of broken communication, it is the client's job to re-establish connection.
If you want to see working code examples where server sends data, check out Indy IdTelnet: the telnet client uses a thread to listen to server messages. There is only one socket, created by the client, but the server uses the same socket for its messages to the client, at any time.
The client starts the connection, but does not have to start a conversation by saying 'HELLO' or something like that.
Technically, the client only needs to open the socket connection, without sending any additional data. The client can remain quiet as long as he wants, even until the end of the connection.
The server has a socket connection to the client as soon as the client has connected. And over this socket, the server can send data to the client.
Of course, the client has to read from the connection socket to see the server data. This can be done in a loop in a background thread, or even in the main thread (not in a VCL application of course as it would block).
Finally, this is the code that I used to solve my problem:
// Thread at client-side
procedure FNotifRecieverThread.Execute;
var
str: string;
MID: Integer;
TCP1: TIdTCPClient;
begin
if frmRecieverMain.IdTCPClient1.Connected then
begin
TCP1 := TIdTCPClient.Create(nil);
TCP1.Host := frmRecieverMain.IdTCPClient1.Host;
TCP1.Port := frmRecieverMain.IdTCPClient1.Port;
TCP1.ConnectTimeout := 20000;
while True do
begin
try
TCP1.Connect;
while True do
begin
try
str := '';
TCP1.SendCmd('checkmynotif');
TCP1.Socket.WriteLn(IntToStr(frmRecieverMain.UserID));
str := TCP1.Socket.ReadLn;
if Pos('showmessage_', str) = 1 then
begin
MID := StrToInt(Copy(str, Pos('_', str) + 1, 5));
frmRecieverMain.NotifyMessage(MID);
end
else
if str = 'updateusers' then
begin
LoadUsers;
frmRecieverMain.sgMsgInbox.Invalidate;
frmRecieverMain.sgMsgSent.Invalidate;
frmRecieverMain.cbReceipent.Invalidate;
end
else
if str = 'updatemessages' then
begin
LoadMessages;
frmRecieverMain.DisplayMessages;
end;
except
// be quite and try next time :D
end;
Sleep(2000);
end;
finally
TCP1.Disconnect;
TCP1.Free;
end;
Sleep(5000);
end;
end;
end;
// And command handlers at server-side
procedure TfrmServer.cmhCheckMyNotifCommand(ASender: TIdCommand);
var
UserID, i: Integer;
str: string;
begin
str := 'notifnotfound';
UserID := StrToIntDef(ASender.Context.Connection.Socket.ReadLn, -1);
for i := 0 to NotificationStack.Count - 1 do
if NotificationStack.Notifs[i].Active and
(NotificationStack.Notifs[i].UserID = UserID)
then
begin
NotificationStack.Notifs[i].Active := False;
str := NotificationStack.Notifs[i].NotiffText;
Break;
end;
ASender.Context.Connection.Socket.WriteLn(str);
end;
// And when i want to some client notificated from server, I use some methodes like this:
procedure TfrmServer.cmhSetUserOnlineCommand(ASender: TIdCommand);
var
UserID, i: Integer;
begin
UserID := StrToIntDef(ASender.Context.Connection.Socket.ReadLn, -1);
if UserID <> -1 then
begin
for i := 0 to OnLineUsersCount - 1 do // search for duplication...
if OnLineUsers[i].Active and (OnLineUsers[i].UserID = UserID) then
Exit; // duplication rejected!
Inc(OnLineUsersCount);
SetLength(OnLineUsers, OnLineUsersCount);
OnLineUsers[OnLineUsersCount - 1].UserID := UserID;
OnLineUsers[OnLineUsersCount - 1].Context := ASender.Context;
OnLineUsers[OnLineUsersCount - 1].Active := True;
for i := 0 to OnLineUsersCount - 1 do // notify all other users for refresh users list
if OnLineUsers[i].Active and (OnLineUsers[i].UserID <> UserID) then
begin
Inc(NotificationStack.Count);
SetLength(NotificationStack.Notifs, NotificationStack.Count);
NotificationStack.Notifs[NotificationStack.Count - 1].UserID := OnLineUsers[i].UserID;
NotificationStack.Notifs[NotificationStack.Count - 1].NotiffText := 'updateusers';
NotificationStack.Notifs[NotificationStack.Count - 1].Active := True;
end;
end;
end;

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

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)
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.
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;
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;

Resources