2009-09-29 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / tools / sockpol / sockpol.cs
blob2a7a225160c5dbf9a55da45bcecac70f1bb64202
1 //
2 // Socket Policy Server (sockpol)
3 //
4 // Contact:
5 // Moonlight List (moonlight-list@lists.ximian.com)
6 //
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
8 //
9 // Based on XSP source code (ApplicationServer.cs and XSPWebSource.cs)
10 // Authors:
11 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
12 // Lluis Sanchez Gual (lluis@ximian.com)
14 // Copyright (c) Copyright 2002-2007 Novell, Inc
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System;
37 using System.IO;
38 using System.Net;
39 using System.Net.Sockets;
40 using System.Text;
41 using System.Threading;
43 class SocketPolicyServer {
45 const string PolicyFileRequest = "<policy-file-request/>";
46 static byte[] request = Encoding.UTF8.GetBytes (PolicyFileRequest);
47 private byte[] policy;
49 private Socket listen_socket;
50 private Thread runner;
52 private AsyncCallback accept_cb;
54 class Request {
55 public Request (Socket s)
57 Socket = s;
58 // the only answer to a single request (so it's always the same length)
59 Buffer = new byte [request.Length];
60 Length = 0;
63 public Socket Socket { get; private set; }
64 public byte[] Buffer { get; set; }
65 public int Length { get; set; }
68 public SocketPolicyServer (string xml)
70 // transform the policy to a byte array (a single time)
71 policy = Encoding.UTF8.GetBytes (xml);
74 public int Start ()
76 try {
77 listen_socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
78 listen_socket.Bind (new IPEndPoint (IPAddress.Any, 943));
79 listen_socket.Listen (500);
80 listen_socket.Blocking = false;
82 catch (SocketException se) {
83 // Most common mistake: port 943 is not user accessible on unix-like operating systems
84 if (se.SocketErrorCode == SocketError.AccessDenied) {
85 Console.WriteLine ("NOTE: must be run as root since the server listen to port 943");
86 return 5;
87 } else {
88 Console.WriteLine (se);
89 return 6;
93 runner = new Thread (new ThreadStart (RunServer));
94 runner.Start ();
95 return 0;
98 void RunServer ()
100 accept_cb = new AsyncCallback (OnAccept);
101 listen_socket.BeginAccept (accept_cb, null);
103 while (true) // Just sleep until we're aborted.
104 Thread.Sleep (1000000);
107 void OnAccept (IAsyncResult ar)
109 Socket accepted = null;
110 try {
111 accepted = listen_socket.EndAccept (ar);
112 } catch {
113 } finally {
114 listen_socket.BeginAccept (accept_cb, null);
117 if (accepted == null)
118 return;
120 accepted.Blocking = true;
122 Request request = new Request (accepted);
123 accepted.BeginReceive (request.Buffer, 0, request.Length, SocketFlags.None, new AsyncCallback (OnReceive), request);
126 void OnReceive (IAsyncResult ar)
128 Request r = (ar.AsyncState as Request);
129 Socket socket = r.Socket;
130 try {
131 r.Length += socket.EndReceive (ar);
133 // compare incoming data with expected request
134 for (int i=0; i < r.Length; i++) {
135 if (r.Buffer [i] != request [i]) {
136 // invalid request, close socket
137 socket.Close ();
138 return;
142 if (r.Length == request.Length) {
143 // request complete, send policy
144 socket.BeginSend (policy, 0, policy.Length, SocketFlags.None, new AsyncCallback (OnSend), socket);
145 } else {
146 // continue reading from socket
147 socket.BeginReceive (r.Buffer, r.Length, request.Length - r.Length, SocketFlags.None,
148 new AsyncCallback (OnReceive), ar.AsyncState);
150 } catch {
151 // if anything goes wrong we stop our connection by closing the socket
152 socket.Close ();
156 void OnSend (IAsyncResult ar)
158 Socket socket = (ar.AsyncState as Socket);
159 try {
160 socket.EndSend (ar);
161 } catch {
162 // whatever happens we close the socket
163 } finally {
164 socket.Close ();
168 public void Stop ()
170 runner.Abort ();
171 listen_socket.Close ();
174 const string AllPolicy = @"<?xml version=""1.0"" encoding=""utf-8""?>
175 <access-policy>
176 <cross-domain-access>
177 <policy>
178 <allow-from>
179 <domain uri=""*"" />
180 </allow-from>
181 <grant-to>
182 <socket-resource port=""4502-4534"" protocol=""tcp"" />
183 </grant-to>
184 </policy>
185 </cross-domain-access>
186 </access-policy>";
188 const string LocalPolicy = @"<?xml version=""1.0"" encoding=""utf-8""?>
189 <access-policy>
190 <cross-domain-access>
191 <policy>
192 <allow-from>
193 <domain uri=""file:///"" />
194 </allow-from>
195 <grant-to>
196 <socket-resource port=""4502-4534"" protocol=""tcp"" />
197 </grant-to>
198 </policy>
199 </cross-domain-access>
200 </access-policy>";
202 static int Main (string[] args)
204 if (args.Length == 0) {
205 Console.WriteLine ("sockpol [--all | --local | --file policy]");
206 Console.WriteLine ("\t--all Allow access to all URI on every port (4502-4534)");
207 Console.WriteLine ("\t--local Allow local access on every port (4502-4534)");
208 return 1;
211 string policy = null;
212 switch (args [0]) {
213 case "--all":
214 policy = AllPolicy;
215 break;
216 case "--local":
217 policy = LocalPolicy;
218 break;
219 case "--file":
220 if (args.Length < 2) {
221 Console.WriteLine ("Missing policy file name after '--file'.");
222 return 2;
224 string filename = args [1];
225 if (!File.Exists (filename)) {
226 Console.WriteLine ("Could not find policy file '{0}'.", filename);
227 return 3;
229 using (StreamReader sr = new StreamReader (filename)) {
230 policy = sr.ReadToEnd ();
232 break;
233 default:
234 Console.WriteLine ("Unknown option '{0}'.", args [0]);
235 return 4;
238 SocketPolicyServer server = new SocketPolicyServer (policy);
239 int result = server.Start ();
240 if (result != 0)
241 return result;
243 Console.WriteLine ("Hit Return to stop the server.");
244 Console.ReadLine ();
245 server.Stop ();
246 return 0;