Commit 6a9937d2166023c370489d8e6654d01f6ec52f44 is not correct. WebClient.Proxy shoul...
[mono.git] / mcs / class / System / System.Net / EndPointListener.cs
1 //
2 // System.Net.EndPointListener
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if NET_2_0 && SECURITY_DEP
30
31 using System.IO;
32 using System.Net.Sockets;
33 using System.Collections;
34 using System.Security.Cryptography;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Threading;
37 using Mono.Security.Authenticode;
38
39 namespace System.Net {
40         sealed class EndPointListener
41         {
42                 IPEndPoint endpoint;
43                 Socket sock;
44                 Hashtable prefixes;  // Dictionary <ListenerPrefix, HttpListener>
45                 ArrayList unhandled; // List<ListenerPrefix> unhandled; host = '*'
46                 ArrayList all;       // List<ListenerPrefix> all;  host = '+'
47                 X509Certificate2 cert;
48                 AsymmetricAlgorithm key;
49                 bool secure;
50                 Hashtable unregistered;
51
52                 public EndPointListener (IPAddress addr, int port, bool secure)
53                 {
54                         if (secure) {
55                                 this.secure = secure;
56                                 LoadCertificateAndKey (addr, port);
57                         }
58
59                         endpoint = new IPEndPoint (addr, port);
60                         sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
61                         sock.Bind (endpoint);
62                         sock.Listen (500);
63                         SocketAsyncEventArgs args = new SocketAsyncEventArgs ();
64                         args.UserToken = this;
65                         args.Completed += OnAccept;
66                         sock.AcceptAsync (args);
67                         prefixes = new Hashtable ();
68                         unregistered = Hashtable.Synchronized (new Hashtable ());
69                 }
70
71                 void LoadCertificateAndKey (IPAddress addr, int port)
72                 {
73                         // Actually load the certificate
74                         try {
75                                 string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
76                                 string path = Path.Combine (dirname, ".mono");
77                                 path = Path.Combine (path, "httplistener");
78                                 string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
79                                 string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
80                                 cert = new X509Certificate2 (cert_file);
81                                 key = PrivateKey.CreateFromFile (pvk_file).RSA;
82                         } catch {
83                                 // ignore errors
84                         }
85                 }
86
87                 static void OnAccept (object sender, EventArgs e)
88                 {
89                         SocketAsyncEventArgs args = (SocketAsyncEventArgs) e;
90                         EndPointListener epl = (EndPointListener) args.UserToken;
91                         Socket accepted = null;
92                         if (args.SocketError == SocketError.Success) {
93                                 accepted = args.AcceptSocket;
94                                 args.AcceptSocket = null;
95                         }
96
97                         try {
98                                 if (epl.sock != null)
99                                         epl.sock.AcceptAsync (args);
100                         } catch {
101                                 if (accepted != null) {
102                                         try {
103                                                 accepted.Close ();
104                                         } catch {}
105                                         accepted = null;
106                                 }
107                         } 
108
109                         if (accepted == null)
110                                 return;
111
112                         if (epl.secure && (epl.cert == null || epl.key == null)) {
113                                 accepted.Close ();
114                                 return;
115                         }
116                         HttpConnection conn = new HttpConnection (accepted, epl, epl.secure, epl.cert, epl.key);
117                         epl.unregistered [conn] = conn;
118                         conn.BeginReadRequest ();
119                 }
120
121                 internal void RemoveConnection (HttpConnection conn)
122                 {
123                         unregistered.Remove (conn);
124                 }
125
126                 public bool BindContext (HttpListenerContext context)
127                 {
128                         HttpListenerRequest req = context.Request;
129                         ListenerPrefix prefix;
130                         HttpListener listener = SearchListener (req.Url, out prefix);
131                         if (listener == null)
132                                 return false;
133
134                         context.Listener = listener;
135                         context.Connection.Prefix = prefix;
136                         return true;
137                 }
138
139                 public void UnbindContext (HttpListenerContext context)
140                 {
141                         if (context == null || context.Request == null)
142                                 return;
143
144                         context.Listener.UnregisterContext (context);
145                 }
146
147                 HttpListener SearchListener (Uri uri, out ListenerPrefix prefix)
148                 {
149                         prefix = null;
150                         if (uri == null)
151                                 return null;
152
153                         string host = uri.Host;
154                         int port = uri.Port;
155                         string path = HttpUtility.UrlDecode (uri.AbsolutePath);
156                         string path_slash = path [path.Length - 1] == '/' ? path : path + "/";
157                         
158                         HttpListener best_match = null;
159                         int best_length = -1;
160
161                         if (host != null && host != "") {
162                                 Hashtable p_ro = prefixes;
163                                 foreach (ListenerPrefix p in p_ro.Keys) {
164                                         string ppath = p.Path;
165                                         if (ppath.Length < best_length)
166                                                 continue;
167
168                                         if (p.Host != host || p.Port != port)
169                                                 continue;
170
171                                         if (path.StartsWith (ppath) || path_slash.StartsWith (ppath)) {
172                                                 best_length = ppath.Length;
173                                                 best_match = (HttpListener) p_ro [p];
174                                                 prefix = p;
175                                         }
176                                 }
177                                 if (best_length != -1)
178                                         return best_match;
179                         }
180
181                         ArrayList list = unhandled;
182                         best_match = MatchFromList (host, path, list, out prefix);
183                         if (path != path_slash && best_match == null)
184                                 best_match = MatchFromList (host, path_slash, list, out prefix);
185                         if (best_match != null)
186                                 return best_match;
187
188                         list = all;
189                         best_match = MatchFromList (host, path, list, out prefix);
190                         if (path != path_slash && best_match == null)
191                                 best_match = MatchFromList (host, path_slash, list, out prefix);
192                         if (best_match != null)
193                                 return best_match;
194
195                         return null;
196                 }
197
198                 HttpListener MatchFromList (string host, string path, ArrayList list, out ListenerPrefix prefix)
199                 {
200                         prefix = null;
201                         if (list == null)
202                                 return null;
203
204                         HttpListener best_match = null;
205                         int best_length = -1;
206                         
207                         foreach (ListenerPrefix p in list) {
208                                 string ppath = p.Path;
209                                 if (ppath.Length < best_length)
210                                         continue;
211
212                                 if (path.StartsWith (ppath)) {
213                                         best_length = ppath.Length;
214                                         best_match = p.Listener;
215                                         prefix = p;
216                                 }
217                         }
218
219                         return best_match;
220                 }
221
222                 void AddSpecial (ArrayList coll, ListenerPrefix prefix)
223                 {
224                         if (coll == null)
225                                 return;
226
227                         foreach (ListenerPrefix p in coll) {
228                                 if (p.Path == prefix.Path) //TODO: code
229                                         throw new HttpListenerException (400, "Prefix already in use.");
230                         }
231                         coll.Add (prefix);
232                 }
233
234                 bool RemoveSpecial (ArrayList coll, ListenerPrefix prefix)
235                 {
236                         if (coll == null)
237                                 return false;
238
239                         int c = coll.Count;
240                         for (int i = 0; i < c; i++) {
241                                 ListenerPrefix p = (ListenerPrefix) coll [i];
242                                 if (p.Path == prefix.Path) {
243                                         coll.RemoveAt (i);
244                                         return true;
245                                 }
246                         }
247                         return false;
248                 }
249
250                 void CheckIfRemove ()
251                 {
252                         if (prefixes.Count > 0)
253                                 return;
254
255                         ArrayList list = unhandled;
256                         if (list != null && list.Count > 0)
257                                 return;
258
259                         list = all;
260                         if (list != null && list.Count > 0)
261                                 return;
262
263                         EndPointManager.RemoveEndPoint (this, endpoint);
264                 }
265
266                 public void Close ()
267                 {
268                         sock.Close ();
269                         lock (unregistered.SyncRoot) {
270                                 foreach (HttpConnection c in unregistered.Keys)
271                                         c.Close (true);
272                                 unregistered.Clear ();
273                         }
274                 }
275
276                 public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
277                 {
278                         ArrayList current;
279                         ArrayList future;
280                         if (prefix.Host == "*") {
281                                 do {
282                                         current = unhandled;
283                                         future = (current != null) ? (ArrayList) current.Clone () : new ArrayList ();
284                                         prefix.Listener = listener;
285                                         AddSpecial (future, prefix);
286                                 } while (Interlocked.CompareExchange (ref unhandled, future, current) != current);
287                                 return;
288                         }
289
290                         if (prefix.Host == "+") {
291                                 do {
292                                         current = all;
293                                         future = (current != null) ? (ArrayList) current.Clone () : new ArrayList ();
294                                         prefix.Listener = listener;
295                                         AddSpecial (future, prefix);
296                                 } while (Interlocked.CompareExchange (ref all, future, current) != current);
297                                 return;
298                         }
299
300                         Hashtable prefs, p2;
301                         do {
302                                 prefs = prefixes;
303                                 if (prefs.ContainsKey (prefix)) {
304                                         HttpListener other = (HttpListener) prefs [prefix];
305                                         if (other != listener) // TODO: code.
306                                                 throw new HttpListenerException (400, "There's another listener for " + prefix);
307                                         return;
308                                 }
309                                 p2 = (Hashtable) prefs.Clone ();
310                                 p2 [prefix] = listener;
311                         } while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs);
312                 }
313
314                 public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
315                 {
316                         ArrayList current;
317                         ArrayList future;
318                         if (prefix.Host == "*") {
319                                 do {
320                                         current = unhandled;
321                                         future = (current != null) ? (ArrayList) current.Clone () : new ArrayList ();
322                                         if (!RemoveSpecial (future, prefix))
323                                                 break; // Prefix not found
324                                 } while (Interlocked.CompareExchange (ref unhandled, future, current) != current);
325                                 CheckIfRemove ();
326                                 return;
327                         }
328
329                         if (prefix.Host == "+") {
330                                 do {
331                                         current = all;
332                                         future = (current != null) ? (ArrayList) current.Clone () : new ArrayList ();
333                                         if (!RemoveSpecial (future, prefix))
334                                                 break; // Prefix not found
335                                 } while (Interlocked.CompareExchange (ref all, future, current) != current);
336                                 CheckIfRemove ();
337                                 return;
338                         }
339
340                         Hashtable prefs, p2;
341                         do {
342                                 prefs = prefixes;
343                                 if (!prefs.ContainsKey (prefix))
344                                         break;
345
346                                 p2 = (Hashtable) prefs.Clone ();
347                                 p2.Remove (prefix);
348                         } while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs);
349                         CheckIfRemove ();
350                 }
351         }
352 }
353 #endif
354