LAN Web TCP Delphi

Title: How to write a TCP Redirector using Indy
Question: Many people ask how to write servers in Indy, this primer goes through how a TCP server is created, and how to redirect all traffic to another remote server. This is the same as port-mapping in firewalls.
Answer:
TCP Protocol Redirection Primer
===============================
Contents
1 Introduction
1.1 Disclaimer
2 Writing the Server
2.1 Writing the servers OnExecute() method
2.2 Writing the client connection
2.3 Forwarding data to/from the server/client
2.4 Testing the application
3 Where to next ?
--------------------------------------------------------------------
1 Introduction
--------------------------------------------------------------------
This article explains how to create a TCP protocol redirecter with
Delphi 5 or Delphi 6, using the Indy TCP components.
The purpose of this is to show how to accept a real client
connection to an IdTCPServer, and then how to further connect to
another remote server, and forward all data coming/going to/from
the client/server.
In the article well use the resulting application to redirect
traffic to the delphi3000.com www site, to port 80. The local port
will also be 80, so in effect all traffic coming in on port 80 will
be forwarded to delphi3000.com.
--------------------------------------------------------------------
1.1 Disclaimer
--------------------------------------------------------------------
This article is written by Kim Sandell, September 2002. You can
reach me via email at "sandell@celarius.com" if you have
comments or corrections to the material.
This material is free. It may be used in any way the reader
sees fit, but at the readers own risk.
--------------------------------------------------------------------
2 Writing the Server
--------------------------------------------------------------------
In order to write the server, we need to have a new
application in delphi, the IdTCPServer component dropped
onto the form, and two buttons, one Button1 as a Start
button and another Button2 as a Stop button.
The first thing we need to define is the port the IdTCPServer
component will listen to. This is done by setting the DefaultPort
property of the IcTCPServer1 to 80. (80=www port remember!)
We also need to define the basic functions for the buttons,
and create an event handler for incoming client connections.
For the Button1, create an OnClick event handler:
procedure TForm1.Button1Click(Sender: TObject);
begin
IdTCPServer1.Active := True;
end;
and for the Button2, create an OnClick event handler:
procedure TForm1.Button2Click(Sender: TObject);
begin
IdTCPServer1.Active := False;
end;
Go to the properties of the Events of the IdTCPServer and
create an event handler for the OnExecute event.
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
end;

--------------------------------------------------------------------
2.1 Writing the servers OnExecute() method
--------------------------------------------------------------------
The OnExecute() event is the place where the client connects to.
The parameter "AThread" that is passed with it, is the actial
thread with all the information about the connection.
In this demo we will use only a few of these, but when you get the
hang of all of this, you can start experimenting a bit with the
AThread object.
First we need to make sure that everything that happends in the
handler is thread-safe. This means we can not update any visual
properties, nor can we use any global variables/objects.
We also want to make sure that if anything goes wrong we can handle
the situation and cleanup after the client.
We will begin by making a "Try Except End" plus a "Try Finally End"
statement wrapped inside each other.
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
Try
Try
Finally
End;
Except
End;
End;
Everything that we do in the OnExecute() method must go inside the
try except statement, and most of it will go in the try finally
statement.
--------------------------------------------------------------------
2.2 Writing the client connection
--------------------------------------------------------------------
To make a TCP Client connection to another server/machine we need
the IdTCPClient component. We have to make a local variable so that
we are thread-safe (see above). This component is found in the
IdTCPClient pas file, so we need to include that in the Uses clause
in the "implementation" section:
.....
var
Form1: TForm1;
implementation
Uses IdTCPClient; // This line includes the correct component
{$R *.dfm}
....
The TCPClient variable well use is called CLI. This has to be
declared in the OnExecute() method locally. Also we need to create
the component, and make sure it is destroyed once the connection
is terminated.
We also want to tell it where to connect to once a client connects
to the server, so well set the Host and Port properties of it.
In the finallt statement we want to make sure the real client
that connected is also disconnected, so well throw in a Disconnect
for the AThread.Connection as well.
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
Cli : TIdTCPClient;
begin
Try
Cli := NIL;
Try
{ Create & Connect to Server }
Cli := TIdTCPClient.Create( NIL );
Cli.Host := 'www.delphi3000.com';
Cli.Port := 80;
Finally
If Assigned(Cli) then
Begin
Cli.Disconnect;
Cli.Free;
End;
{ Disconnect real client }
AThread.Connection.Disconnect;
End;
Except
End;
End;
Now when a client connects to us, we need to establish a connection
to the remote server, so we immediately try to connect to that
server using the Cli.Connect; method.
...
Try
{ Create & Connect to Server }
Cli := TIdTCPClient.Create( NIL );
Cli.Host := 'www.delphi3000.com';
Cli.Port := 80;
{ Connect to the remote server }
Cli.Connect;
Finally
...

If we get an error (exception) here, the code will jump to the
finally statement, where the CLI component is freed, and the
connection to the Client is disconnected immediately. In effect
the only thing the real client sees is that a connection was made
but the connection was immediately lost before any data came
through.
--------------------------------------------------------------------
2.3 Forwarding data to/from the server/client
--------------------------------------------------------------------
Now that we have the real client connected, and we also have a
connection to the remote server, we want to start forwarding data
to/from the client/server.
This will be done for as long as both parties are connected to
us, so well need a loop that keeps checking if data is coming or
going.
In the loop well check if the real client has any data to send, and
also check if the remote server as any data to send to the client.
We also need to check for disconnection on both ends, since the
Indy components do not alwyas notify of disconnections.
Last but not least: We need to make sure the loop does not take
100% CPU time, since it is going pretty fast through the loop.
To avoid that we will add a Sleep command at the end of the loop.
The complete OnExecute() handler should now look like this:
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
Var
Cli : TIdTCPClient;
Len : Cardinal;
Data : String;
begin
Try
Cli := NIL;
Try
{ Create & Connect to Server }
Cli := TIdTCPClient.Create( NIL );
Cli.Host := 'www.delphi3000.com';
Cli.Port := 80;
{ Connect to the remote server }
Cli.Connect;
{ Read/Write loop }
Repeat
{ Read data from Client }
{ Uncomment the line below, depending on your
INDY version }
// 0 then
// 9: If Lenght(AThread.Connection.CurrentReadBuffer)0 then
Begin
Len := AThread.Connection.CurrentReadBufferSize;
Data := AThread.Connection.ReadString(Len);
{ Write it to the Server }
Cli.Write( Data );
End;
{ Read data from Server }
If Cli.CurrentReadBufferSize0 then
Begin
Len := Cli.CurrentReadBufferSize;
Data := Cli.ReadString(Len);
{ Write it to the Server }
AThread.Connection.Write( Data );
End;
{ Check for Disconnects }
Cli.CheckForDisconnect(False);
Cli.CheckForGracefulDisconnect(False);
AThread.Connection.CheckForDisconnect(False);
AThread.Connection.CheckForGracefulDisconnect(False);
{ Release system slizes }
SleepEx(1,True);
Until (NOT AThread.Connection.Connected) OR (NOT Cli.Connected);
Finally
If Assigned(Cli) then
Begin
Cli.Disconnect;
Cli.Free
End;
{ Disconnect real client }
AThread.Connection.Disconnect;
End;
Except End;
end;
--------------------------------------------------------------------
2.4 Testing the application
--------------------------------------------------------------------
Compile and run the application.
Try connecting with your browser to the "http://localhost" address.
You should see the delphi3000.com website !!!
Note: This does not work if you must have a proxy defined in your
browser !!
--------------------------------------------------------------------
3 Where to next ?
--------------------------------------------------------------------
Now that you have learned the basics of redirecting TCP
connections, I think the next logial step would be to expand the
application with more features.
A few suggestions for small enhancements:
- Max number of connected users at one time
- A timeout disconnect if nothing happends (no data transferred).
- Recognise protocols, such as HTTP and actually block unwanted
url's from beeing accessed. This would almost be a proxy, even
thoug a proxy needs to check where the client wants to go first.
Some of the suggestions are propably easy to implement, others
harder. The level of expertiese the programmer (you) has really
is the only limit here, and if you want to learn something new,
then learning by doing is the best way.
Enjoy the article, and have a nice day.