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
Comments
- Anonymous
November 18, 2008
PingBack from http://www.tmao.info/silverlight-tip-of-the-day-12-full-implementation-of-a-silverlight-policy-server/