Sunday, November 16, 2008

Silverlight - Policy Server in VB.NET

Silver Light Policy Server
For Socket based communications in Silverlight, we need to run Policy Server at Socket Listening host. Policy Server will send the Policy XML file to silverlight client similar to flash crossdomain.xml file, which Silverlight also uses for its HTTP request. Without Policy Server, Silverlight sockets cannot connect to a Server Application.

Why we need a separate Policy file for sockets ?
The Crossdomain.xml file can be directly accessed by Clients using HTTP request from related web server, where as for sockets based communication server,  web server is not in the picture. So for security purposes, similar to crossdomain file, there needs a policy file to be downloaded & which will be issued by Policy Server.

Ports
Policy Server Listening Port: 943
Silverlight Sockets Allowed Ports: 4502 to 4534

How it will work ?
1. Our Policy Server listens in Port: 943. 
2. Silverlight client will connect to our policy server & send a request "<policy-file-request/>"
3. Once this request is received, Policy Server should send the Policy XML file in that connection & close that connection request.

Sample Policy XML File
clientaccesspolicy.xml

<?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>


domain uri - Allows the domain from where it can be called. "*" will allow all domains.
To allow only a particular domain Ex: uri="http://srimax.com"/>

Socket-Resource port can be used to indicate the allowed ports.
To allow only a particular port: port="4512" ...

Policy Server
PolicyServerService.vb


Imports System
Imports System.IO
Imports System.Net.Sockets
Imports System.Text

Public Class PolicyServerService

    Public sSocket As Socket
    Private PolicyFile As String = String.Empty
    Private Const POLICYREQUEST As String = "<policy-file-request/>"
    Private Const POLICYPORT As Integer = 943
    Private Const POLICYFILENAME As String = "clientaccesspolicy.xml" 

    Public Sub New()
        ' Read Policy File details
        PolicyFile = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) & "\" & POLICYFILENAME

        ' Establish the local endpoint for the socket.
        Dim ipLocal As New System.Net.IPEndPoint(System.Net.IPAddress.Any, POLICYPORT)
        sSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        sSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 0)
sSocket.Bind(ipLocal)

        sSocket.Listen(10)
        'Start an asynchronous socket to listen for connections.
        sSocket.BeginAccept(New AsyncCallback(AddressOf onConnect), Nothing)

    End Sub

 

    Private Sub onConnect(ByVal asyn As IAsyncResult)
        Dim objPolicyConnection As New PolicyConnection
        Try
            With objPolicyConnection
                .cSocket = sSocket.EndAccept(asyn)
                .cSocket.BeginReceive(.buffer, 0, .buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf onDataArrival), objPolicyConnection)
            End With 

            'continue listen for connections.
            sSocket.BeginAccept(New AsyncCallback(AddressOf onConnect), Nothing)

        Catch exNull As System.NullReferenceException
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Sub 

    Private Sub onDataArrival(ByVal asyn As IAsyncResult)
        Dim strData As String = String.Empty
        Dim policyFile As String = String.Empty
        Dim ibRead As Integer = 0
        Dim objPolicyConnection As PolicyConnection = CType(asyn.AsyncState, PolicyConnection)
        Try
            With objPolicyConnection
                If .cSocket.Connected Then ibRead = .cSocket.EndReceive(asyn)
                If ibRead > 0 Then
receivedText.Append(Encoding.ASCII.GetString(.buffer, 0, ibRead))
                strData = .receivedText.ToString()
                If strData.Length < POLICYREQUEST.Length Then
                    ' if not full request received
                    .cSocket.BeginReceive(.buffer, 0, .buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf onDataArrival), objPolicyConnection)

                    Return
                End If
                If StringComparer.InvariantCultureIgnoreCase.Compare(strData, POLICYREQUEST) <> 0 Then
                    ' if not matching with Policy Request, then exit
                    .cSocket.Close()
                    Return
                End If

                'Sending Policy File
                .cSocket.BeginSendFile(policyFile, New AsyncCallback(AddressOf onSend), objPolicyConnection)
            End With

        Catch ex As Exception
            objPolicyConnection.cSocket.Close()
        End Try

    End Sub 

    Private Sub onSend(ByVal asyn As IAsyncResult)
        Dim objPolicyConnection As PolicyConnection = CType(asyn.AsyncState, PolicyConnection)
        ' Finish File Send & Close Connection since no further communication needed
        Try
            objPolicyConnection.cSocket.EndSendFile(asyn)
        Catch ex As Exception
        Finally
            objPolicyConnection.cSocket.Close()
        End Try
    End Sub
End Class

Public Class PolicyConnection
    Public cSocket As Socket
    Private Const POLICYREQUEST As String ="<policy-file-request/>"
    Public buffer(POLICYREQUEST.Length) As Byte
    Public receivedText As New StringBuilder
End Class

This Policy Server Service class can be called from our server application or can be a standalone PolicyServer application by just creating object.
Private objPolicyServer As PolicyServerService
Private Sub StartPolicyServer()
 objPolicyServer = New PolicyServerService()
End Sub


Download

The source code for the above can be downloaded from here

Conclusion
Thus we can have a stand-alone policy server or integerated policy server created in VB.NET.

Happy Silverlight Sockets Programming!

No comments: