简介
policy-file是flash socket安全机制的重要部分,而本文详细介绍提供policy-file的policyserver的实现过程。事实上,policyserver本身也是一个socket服务器端的简单原型。了解本文也将为教程中后续的sessionserver的讨论有所铺垫。
什么是policy-file
policy-file是一个flash的安全控制机制,它的设置决定了是否允许flash程序进行跨域通讯。这是对ajax,dom无法跨域的一个拓展。
一个典型的policy-file可能是这样的:
<?xml version="1.0" encoding="UTF-8"?> <cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"> <allow-access-from domain="*" to-ports="*" secure="false" /> <site-control permitted-cross-domain-policies="master-only" /> </cross-domain-policy>
其中你可以在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 ("<policy-file-request/>/0" == content) {
Send(handler,
@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />
<site-control permitted-cross-domain-policies=""master-only"" />
</cross-domain-policy>
");
} else {
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
写完策略文件后,在控制台写入作为提示信息。并关闭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 ("<policy-file-request/>/0" == content) {
Send(handler,
@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />
<site-control permitted-cross-domain-policies=""master-only"" />
</cross-domain-policy>
");
} 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;
}
}
}

本文深入探讨了Flashsocket安全机制中policy-file的作用、请求过程及异常情况,并详细介绍了policyserver的实现方式,包括监听端口、处理接入请求、读入请求并写回policy-file的过程,同时提供了配置策略的示例。
874

被折叠的 条评论
为什么被折叠?



