`

从头开始构建一个web即时通讯系统(五) - 服务器端 - policyserver

 
阅读更多

简介

policy-fileflash socket安全机制的重要部分,而本文详细介绍提供policy-filepolicyserver的实现过程。事实上,policyserver本身也是一个socket服务器端的简单原型。了解本文也将为教程中后续的sessionserver的讨论有所铺垫。

 

什么是policy-file

policy-file是一个flash的安全控制机制,它的设置决定了是否允许flash程序进行跨域通讯。这是对ajaxdom无法跨域的一个拓展。

一个典型的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和端口前,会先尝试连接该ip843端口

2如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接

3 flash会分析policy-file的内容,并确认是否访问指定的ip和端口是否是被允许的

如果指定的ip和端口是否是被允许的,flash才会真正的连接到指定的ip和端口

 

policy-file的请求过程的异常情况

如果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;
		}
	}
}
 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics