policy-file是flash
socket安全机制的重要部分,而本文详细介绍提供policy-file的policyserver的实现过程。事实上,policyserver本身也是一个socket服务器端的简单原型。了解本文也将为教程中后续的sessionserver的讨论有所铺垫。
什么是policy-file
policy-file是一个flash的安全控制机制,它的设置决定了是否允许flash程序进行跨域通讯。这是对ajax,dom无法跨域的一个拓展。
一个典型的policy-file可能是这样的:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">
to-ports="*"
secure="false"
/>
其中你可以在policy-file指定允许开放的域名/ip,或者是端口。
policy-file的请求过程
1 flash socket在真正执行socket.connect,以连接到指定的ip和端口前,会先尝试连接该ip的843端口
2如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接
3 flash会分析policy-file的内容,并确认是否访问指定的ip和端口是否是被允许的
4 如果指定的ip和端口是否是被允许的,flash才会真正的连接到指定的ip和端口
policy-file的请求过程的异常情况
1 如果843端口连接失败或者超时,flash依然会连接指定端口,并尝试从指定端口获取policy-file
2如果policy-file获取失败,或者指定的ip和端口不在policy-file允许范围内,flash
socket将抛出flash.events.SecurityErrorEvent.SECURITY_ERROR这个异常
policyserver的实现
监听端口
IPAddress ipAddress =
IPAddress.Parse(ip);
IPEndPoint localEndPoint =
new IPEndPoint(ipAddress, 843);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
其中allDone是一个信号控制变量。在接收到一个新的连接请求前,policyserver会阻塞在allDone.WaitOne()这一句。
处理接入请求
public static void
AcceptCallback(IAsyncResult
ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler =
listener.EndAccept(ar);
Console.WriteLine("conneceted : " +
handler.RemoteEndPoint.ToString());
StateObject state =
new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback),
state);
}
在上面的代码里,我还把客户端的ip写入控制台,并将异步接受flash的第一个请求。
读入请求,写回policy-file
当接入一个连接请求以后,allDone.Set()释放信号量,在等待接入的循环体里,将完成一次过程,进入下一次循环。
public static void
ReadCallback(IAsyncResult
ar)
{
String content = String.Empty;
StateObject state =
(StateObject)ar.AsyncState;
Socket handler =
state.workSocket;
int bytesRead =
handler.EndReceive(ar);
if (bytesRead >
0)
{
state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0,
bytesRead));
content = state.sb.ToString();
if ("/0"
== content)
{
Send(handler,
@"
encoding=""UTF-8""?>
xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
domain=""*"" to-ports=""*"" secure=""false""
/>
permitted-cross-domain-policies=""master-only""
/>
"
);
}
else
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback),
state);
}
}
}
policyserver确认第一个请求,是否是"/0",如果是,则将策略文件异步写回给flash。
关闭socket
private static void
SendCallback(IAsyncResult
ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent =
handler.EndSend(ar);
Console.WriteLine("policy file sent");
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
写完策略文件后,在控制台写入作为提示信息。并关闭socket。
配置
考虑到这个policyserver可能部署在不同的环境,为了方便部署,我从文件名中读取配置。
Match m = Regex.Match(Environment.CommandLine, @"/d+/./d+/./d+/./d+");
string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;
Console.WriteLine("binding to " + ip);
比如编译结果文件为SimplePolicyServer.exe,我把它文件名修改为SimplePolicyServer.192.168.1.10.exe
,那么它在执行的时候,便会监听192.168.1.10这个ip的端口。如果没有指定正确的ip的话,默认绑定到本机,即127.0.0.1。
完整代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
public class StateObject
{
public Socket workSocket = null;
public const int
BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
namespace SimplePolicyServer
{
class Program
{
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public static void
StartListening()
{
byte[] bytes = new Byte[1024];
Match m = Regex.Match(Environment.CommandLine, @"/d+/./d+/./d+/./d+");
string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;
Console.WriteLine("binding to " + ip);
IPAddress ipAddress =
IPAddress.Parse(ip);
IPEndPoint localEndPoint =
new IPEndPoint(ipAddress, 843);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.Read();
}
public static void
AcceptCallback(IAsyncResult
ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler =
listener.EndAccept(ar);
Console.WriteLine("conneceted : " +
handler.RemoteEndPoint.ToString());
StateObject state =
new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback),
state);
}
public static void
ReadCallback(IAsyncResult
ar)
{
String content = String.Empty;
StateObject state =
(StateObject)ar.AsyncState;
Socket handler =
state.workSocket;
int bytesRead =
handler.EndReceive(ar);
if (bytesRead >
0)
{
state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0,
bytesRead));
content = state.sb.ToString();
if ("/0"
== content)
{
Send(handler,
@"
encoding=""UTF-8""?>
xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
domain=""*"" to-ports=""*"" secure=""false""
/>
permitted-cross-domain-policies=""master-only""
/>
"
);
}
else
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback),
state);
}
}
}
private static void
Send(Socket handler,
String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback),
handler);
}
private static void
SendCallback(IAsyncResult
ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent =
handler.EndSend(ar);
Console.WriteLine("policy file sent");
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int
Main(String[] args)
{
StartListening();
return 0;
}
}
}