Silverlight Playground
about Silverlight and other Amenities

A proxy class to tunnel Socket channels

2009-04-01T23:00:00+01:00 by Andrea Boschin

One of the requests I found, following the Silverlight forums, is the capability to connect a Socket to an arbitrary port out of the restricted range where Silverlight is allowed to work. Since the release 2.0, Silverlight is allowed to connect to the network using a Socket but has a limitation that forbid the access to ports out of the range from 4502 to 4534. Another requirements for the sockets to work is that the same service must expose a clientaccesspolicy.xml file on a well known port 943 stating the ports opened by the service.

This complicated context let the use of the Sockets difficult and limitated to custom services. The limit of 32 ports opened is fine if someone has to create a custom service giving realtime updates, but is frustrating if the need is to access a well know network service like DNS, Telnet and other. We have to know that some network protocols are impossible to be implemented with Silverlight because of them nature. Thinking about FTP the duplex nature of this service, where server and client exchange their role (the client become server when receiving a file)  let this protocol impossible to be implemented in Silverlight where there is no way to listen to incoming connections. There is a number of other existing services, and often a number of existing custom services that rely on ports out of the 4502-4534 range and that we would like to consume without changing the code because often they are already published to other systems.

In this article I will explain a simple project I've implemented to let some services to be "tunneled" moving their original port to another port in the range allowed to Silverlight. This service that is freely downloadable at the end of the post, also implements a channel on the port 943 to allow Silverlight to get the client access policies before to connect to the tunneled ports.

The system Architecture

Before showing some snippets of code I will try to explain how my solution works. I will go through the architecture showing the components and the reason of my choice. The first concept you may know about implementing Sockets is that when you are listening on a port you are waiting for accepting clients and when a client connect you need to start a thread that take the accepted client and manage it separately from the main listening socket. This behavioh allow you to manage multiple incoming clients giving each connection in charge to a separate thread and leaving the port free to return a the listening task. Another requirement we have developing the solution for Silverlight is to have two servers listening, one for the policy distribution and the other on the proxy service that forward traffic to the required port.

To solve this problem I've created a base class ThreadedObject that implements the base behavior of every subsystem. From this class I've inherited three types that manage the different parts of the problem.

classes

The ThreadedObject class is in charge of managing the lifetime of the thread. It is a IDisposable class so it is created with an using construct and this cause the closing of the thread when the class goes out of scope. The trick is the usage of a ManualResetEvent exposed to the subclasses. When this event will be set the thread has to exit gracefully.

The SocketProxy class

This class have in charge the task to listen for incoming connections on both the policy port 943 and the proxy channel that we will expose on the port 4530. I've created this class thinking about a developer that needs to embed a listening proxy in a Windows service. It is a simple class that request a few parameters in input and when started run a thread that listen. As you may know the parameters tells the host and the ports where to listen and forward connections. In my solution this parameters are read from the app.config file. The constructor wants also the path of a policy file. Here is the main thread procedure of the class:

   1: /// <summary>
   2: /// Listen the thread.
   3: /// </summary>
   4: /// <param name="state">The state.</param>
   5: protected override void ThreadProc()
   6: {
   7:     IPHostEntry hostEntry = Dns.GetHostEntry(this.ListenHost);
   8:     IPAddress ipAddress = hostEntry.AddressList.Where(entry => entry.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault();
   9:  
  10:     if (ipAddress != null)
  11:     {
  12:         this.PolicyListener = new TcpListener(ipAddress, 943);
  13:         this.PolicyListener.Start();
  14:         this.ProxyListener = new TcpListener(ipAddress, this.ListenPort);
  15:         this.ProxyListener.Start();
  16:  
  17:         while (WaitHandle.WaitTimeout == WaitHandle.WaitAny(this.ExitHandles, 10))
  18:         {
  19:             if (this.PolicyListener.Pending())
  20:             {
  21:                 PolicyClient policyClient =
  22:                     new PolicyClient(this.PolicyListener.AcceptTcpClient(), this.PolicyFile);
  23:                 policyClient.Start();
  24:  
  25:             }
  26:             else if (this.ProxyListener.Pending())
  27:             {
  28:                 ProxyClient proxyClient = 
  29:                     new ProxyClient(this.ProxyListener.AcceptTcpClient(), this.TargetHost, this.TargetPort);
  30:                 proxyClient.Start();
  31:             }
  32:         }
  33:     }
  34:     else
  35:         throw new ApplicationException(Resources.CannotResolveAddress);
  36: }

The class starts two instances of TcpListener and then poll the Pending() method to check when there is waiting clients to accept. When a client is ready it starts an instance of the managing type.

The PolicyClient

This class implements a manager for the clients requesting the policies. It will be started by the SocketProxy class when a client ask for a connection. The class operate in two steps: first of all it wait for the client to send the policy request string then it sends the policy file and finally close the connection:

   1: /// <summary>
   2: /// Threads the proc.
   3: /// </summary>
   4: protected override void ThreadProc()
   5: {
   6:     try
   7:     {
   8:         while (WaitHandle.WaitTimeout == WaitHandle.WaitAny(this.ExitHandles, 10) &&
   9:             this.Client.Available == 0) ;
  10:  
  11:         ReadAndSend();
  12:     }
  13:     finally
  14:     {
  15:         this.Client.Close();
  16:     }
  17: }
  18:  
  19: /// <summary>
  20: /// Validates the and send.
  21: /// </summary>
  22: /// <returns></returns>
  23: private void ReadAndSend()
  24: {
  25:     NetworkStream stream = this.Client.GetStream();
  26:  
  27:     byte[] data = new byte[PolicyRequestString.Length];
  28:     stream.Read(data, 0, data.Length);
  29:  
  30:     string readString = Encoding.UTF8.GetString(data);
  31:  
  32:     if (readString == PolicyRequestString)
  33:     {
  34:         stream.Write(this.PolicyFile, 0, this.PolicyFile.Length);
  35:         stream.Flush();
  36:     }
  37: }

The class request in the constructor a byte array of the policy file to send. This buffer is loaded by the SocketProxy class and then decoded to a byte array and passed to all the instances.

The ProxyClient

This is the most critical class that have in charge the clients when tunneling the traffic in both the directions. It will have two clients; the silverlight part and the tunneled service part. It simply wait for traffic from each client and forward it to the other. The operation of tunneling the data is the critical part. I read the data from a channel in 1024 bytes chunks and write the same bytes on the other client. I think this part has to be tested on various services and tuned finding the best strategy. The current implementation may fail when both parts of the channel is data intensive because reading data from a channel stops reading data from the other.

   1: /// <summary>
   2: /// Threads the proc.
   3: /// </summary>
   4: protected override void ThreadProc()
   5: {
   6:     try
   7:     {
   8:         while (WaitHandle.WaitTimeout == WaitHandle.WaitAny(this.ExitHandles, 10) &&
   9:             this.Client.Available == 0) ;
  10:  
  11:         ReadAndSend();
  12:     }
  13:     finally
  14:     {
  15:         this.Client.Close();
  16:     }
  17: }
  18:  
  19: /// <summary>
  20: /// Validates the and send.
  21: /// </summary>
  22: /// <returns></returns>
  23: private void ReadAndSend()
  24: {
  25:     NetworkStream stream = this.Client.GetStream();
  26:  
  27:     byte[] data = new byte[PolicyRequestString.Length];
  28:     stream.Read(data, 0, data.Length);
  29:  
  30:     string readString = Encoding.UTF8.GetString(data);
  31:  
  32:     if (readString == PolicyRequestString)
  33:     {
  34:         stream.Write(this.PolicyFile, 0, this.PolicyFile.Length);
  35:         stream.Flush();
  36:     }
  37: }

Testing the application

To test the class I've selected a service from the canonical ports exposed by a system.  Finally I've selected the daytime port 13. It is a service that on Windows has to be installed under the Simple TCP services and then started by the Services applet. The choice of this simple and slightly unuseful ports is only to give a very simple example that show how to expose a port that is really out of the Silverlight range. My simple application has a button and a TextBlock. When the Button is clicked a Socket connection will be made to appthe daytime service and the returned system time will be displayed in the TextBlock.

The server part is configured to tunnel connection incoming on port 4530 to the system dtaytime port and to serve a policy file from the file system.

I'm thinking to write a more powerful sample on this code. Opening ports to the silverlight applications may open a wide range of solutions limited only by your creativity and probably by your it manager. When exposing a port on the internet you have to concern about security issues. Every opened port to the network is a potential security disclosure but the security must be implemented in the protocol you are exposing. Also you have to know that exposing a well-know port may give an opportunity to use a well know bug to enter your system.

Download: Elite.Silverlight.SocketProxy.zip (1,1 MB)

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading