Silverlight Tip of the Day #12 - Full Implementation of a Silverlight Policy Server.

Before a Silverlight application can connect to a server it must first successfully connect to a policy server on that machine in order to proceed with the connection.

In this tip I will take you through every step you need to create and run your own policy server.

To start, create a new C# console application.  Then, create a new XML file called “clientaccesspolicy.xml” and add it to your project. This is the file your Policy Server will send to a client giving it permission to proceed with the connection.

The contents of the “clientaccesspolicy.xml” are as follows:

 <?xml version="1.0" encoding="utf-8" ?>
 <access-policy>
   <cross-domain-access>
     <policy>
       <allow-from>
         <domain uri="*"/>
       </allow-from>
       <grant-to>
         <socket-resource port="4502-4534" protocol="tcp"/>
       </grant-to>
     </policy>
   </cross-domain-access>
 </access-policy>

Note that with Silverlight, sockets are limited to ports #4502-4534.  Also, the policy server that sends this policy file to the connecting client must be run on port 943.

The code for the policy server itself is as follows:

 using System;
 using System.IO;
 using System.Net;
 using System.Net.Sockets;
  
 namespace PolicyServer
 {
     // Encapsulate and manage state for a single connection from a client
     class PolicyConnection
     {
         private Socket _connection;
         private byte[] _buffer; // buffer to receive the request from the client       
         private int _received;
         private byte[] _policy; // the policy to return to the client
  
         // the request that we're expecting from the client
         private static string _policyRequestString = "<policy-file-request/>";
  
         public PolicyConnection(Socket client, byte[] policy)
         {
             _connection = client;
             _policy = policy;
  
             _buffer = new byte[_policyRequestString.Length];
             _received = 0;
  
             try
             {
                 // receive the request from the client
                 _connection.BeginReceive(_buffer, 0, _policyRequestString.Length, SocketFlags.None,
                     new AsyncCallback(OnReceive), null);
             }
             catch (SocketException)
             {
                 _connection.Close();
             }
         }
  
         // Called when we receive data from the client
         private void OnReceive(IAsyncResult res)
         {
             try
             {
                 _received += _connection.EndReceive(res);
  
                 // if we haven't gotten enough for a full request yet, receive again
                 if (_received < _policyRequestString.Length)
                 {
                     _connection.BeginReceive(_buffer, _received, _policyRequestString.Length - _received,
                         SocketFlags.None, new AsyncCallback(OnReceive), null);
                     return;
                 }
  
                 // make sure the request is valid
                 string request = System.Text.Encoding.UTF8.GetString(_buffer, 0, _received);
                 if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _policyRequestString) != 0)
                 {
                     _connection.Close();
                     return;
                 }
  
                 // send the policy
                 Console.Write("Sending policy...\n");
                 _connection.BeginSend(_policy, 0, _policy.Length, SocketFlags.None,
                     new AsyncCallback(OnSend), null);
             }
             catch (SocketException)
             {
                 _connection.Close();
             }
         }
  
         // called after sending the policy to the client; close the connection.
         public void OnSend(IAsyncResult res)
         {
             try
             {
                 _connection.EndSend(res);
             }
             finally
             {
                 _connection.Close();
             }
         }
     }
  
     // Listens for connections on port 943 and dispatches requests to a PolicyConnection
     class PolicyServer
     {
         private Socket _listener;
         private byte[] _policy;
  
         // pass in the path of an XML file containing the socket policy
         public PolicyServer(string policyFile)
         {
  
             // Load the policy file
             FileStream policyStream = new FileStream(policyFile, FileMode.Open);
  
             _policy = new byte[policyStream.Length];
             policyStream.Read(_policy, 0, _policy.Length);
             policyStream.Close();
  
  
             // Create the Listening Socket
             _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                 ProtocolType.Tcp);
  
             _listener.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)
                 SocketOptionName.NoDelay, 0);
  
             _listener.Bind(new IPEndPoint(IPAddress.Any, 943));
             _listener.Listen(10);
  
             _listener.BeginAccept(new AsyncCallback(OnConnection), null);
         }
  
         // Called when we receive a connection from a client
         public void OnConnection(IAsyncResult res)
         {
             Socket client = null;
  
             try
             {
                 client = _listener.EndAccept(res);
             }
             catch (SocketException)
             {
                 return;
             }
  
             // handle this policy request with a PolicyConnection
             PolicyConnection pc = new PolicyConnection(client, _policy);
  
             // look for more connections
             _listener.BeginAccept(new AsyncCallback(OnConnection), null);
         }
  
         public void Close()
         {
             _listener.Close();
         }
     }
  
     public class Program
     {
         static void Main()
         {
             Console.Write("Starting...\n");
             PolicyServer ps =
                 new PolicyServer(@"<path to your file>\clientaccesspolicy.xml");
             System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
         }
     }
 }

You will need to change where it says "<path to your file>\clientaccesspolicy.xml" to be the actual path to where you saved this file on your server.

Thank you,

--Mike Snow

 Subscribe in a reader

Comments